Merge pull request #166 from WhiteNinjaZ/watch-pwm

Timer and Pulse Width modulation examples
This commit is contained in:
Maciej Kurc 2021-09-10 10:14:56 +02:00 committed by GitHub
commit 07a6353627
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 432 additions and 3 deletions

View File

@ -47,7 +47,7 @@ shift
examples="$@"
if [ "$fpga_family" == "xc7" -a -z "$examples" ]; then
examples="counter picosoc litex litex_linux button_controller"
examples="counter picosoc litex litex_linux button_controller timer pulse_width_led"
elif [ "$fpga_family" == "eos-s3" -a -z "$examples" ]; then
examples="counter"
fi
@ -78,6 +78,12 @@ if [ "$fpga_family" = "xc7" ]; then
"button_controller")
snippets="${additionalDesigns} xc7/additional_examples/button_controller/README.rst:example-debouncer-basys3"
;;
"pulse_width_led")
snippets="${snippets} xc7/pulse_width_led/README.rst:example-pulse-arty-35t"
;;
"timer")
snippets="${snippets} xc7/timer/README.rst:example-watch-basys3"
;;
*)
echo "ERROR: Unknown example name: $example" >&2
exit 1

View File

@ -67,6 +67,24 @@ jobs:
- {fpga-fam: "xc7", os: "debian", os-version: "bullseye", example: "button_controller"}
- {fpga-fam: "xc7", os: "debian", os-version: "sid", example: "button_controller"}
- {fpga-fam: "xc7", os: "ubuntu", os-version: "xenial", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "ubuntu", os-version: "bionic", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "ubuntu", os-version: "focal", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "centos", os-version: "7", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "centos", os-version: "8", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "debian", os-version: "buster", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "debian", os-version: "bullseye", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "debian", os-version: "sid", example: "pulse_width_led"}
- {fpga-fam: "xc7", os: "ubuntu", os-version: "xenial", example: "timer"}
- {fpga-fam: "xc7", os: "ubuntu", os-version: "bionic", example: "timer"}
- {fpga-fam: "xc7", os: "ubuntu", os-version: "focal", example: "timer"}
- {fpga-fam: "xc7", os: "centos", os-version: "7", example: "timer"}
- {fpga-fam: "xc7", os: "centos", os-version: "8", example: "timer"}
- {fpga-fam: "xc7", os: "debian", os-version: "buster", example: "timer"}
- {fpga-fam: "xc7", os: "debian", os-version: "bullseye", example: "timer"}
- {fpga-fam: "xc7", os: "debian", os-version: "sid", example: "timer"}
env:
LANG: "en_US.UTF-8"
DEBIAN_FRONTEND: "noninteractive"

View File

@ -91,6 +91,14 @@ Enter the directory that contains examples for Xilinx 7-Series FPGAs:
.. jinja:: xc7_linux_litex_demo
:file: templates/example.jinja
.. jinja:: xc7_timer
:file: templates/example.jinja
.. jinja:: xc7_pulse_width_led
:file: templates/example.jinja
Additional Examples
-------------------
@ -102,6 +110,7 @@ for the basys3 board in the additional_examples directory:
cd additional_examples
QuickLogic EOS S3
-----------------

View File

@ -30,7 +30,6 @@ module display_control (
(anode_select == 2'b10) ? 4'b1011 :
4'b0111;
assign anode = cur_anode | (~digitDisplay);
assign cur_data_in =

View File

@ -0,0 +1,9 @@
current_dir := ${CURDIR}
TARGET := arty_35
TOP := top
SOURCES := ${current_dir}/PWM.v
SOURCES += ${current_dir}/pulse_led.v
XDC := ${current_dir}/arty_35.xdc
include ${current_dir}/../../common/Makefile

14
xc7/pulse_width_led/PWM.v Normal file
View File

@ -0,0 +1,14 @@
module PWM (
input wire clk,
input wire [13:0] width,
output reg pulse
);
reg [13:0] counter = 0;
always @(posedge clk) begin
counter <= counter + 1;
if (counter < width) pulse <= 1'b1;
else pulse <= 1'b0;
end
endmodule

View File

@ -0,0 +1,36 @@
Pulse Width
~~~~~~~~~~~~
This example is built specificity for the arty_35T. It demonstrates a greater variety of I/O and
a PWM that drives the RGB leds on the board. To build this example run the following
commands:
.. code-block:: bash
:name: example-pulse-arty-35t
make -C pulse_width_led
At completion, the bitstreams are located in the build directory:
.. code-block:: bash
cd pulse_width_led/build/arty_35
Now, you can upload the design with:
.. code-block:: bash
openocd -f ${INSTALL_DIR}/${FPGA_FAM}/conda/envs/${FPGA_FAM}/share/openocd/scripts/board/digilent_arty.cfg -c "init; pld load 0 top.bit; exit"
After downloading the bitstream, you can experiment with and mix different amounts of red, green, and
blue on RGB led 0 by toggling different switches and buttons on and off. From left to right:
switches 3, 2, 1 control the intensity of blue, switch 0 and buttons 3 and 2 control the intensity of
red, and buttons 1 and 0 control the intensity of green. The following provides an example:
.. image:: ../../docs/images/pwm.gif
:align: center
:width: 50%

View File

@ -0,0 +1,26 @@
# Clock signal
set_property PACKAGE_PIN E3 [get_ports { clk }];
set_property IOSTANDARD LVCMOS33 [get_ports { clk }];
# Switches
set_property -dict { PACKAGE_PIN A8 IOSTANDARD LVCMOS33 } [get_ports { sw[0] }];
set_property -dict { PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }];
set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }];
set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }];
# RGB LEDs
set_property -dict { PACKAGE_PIN E1 IOSTANDARD LVCMOS33 } [get_ports { pulse_blue }];
set_property -dict { PACKAGE_PIN F6 IOSTANDARD LVCMOS33 } [get_ports { pulse_green }];
set_property -dict { PACKAGE_PIN G6 IOSTANDARD LVCMOS33 } [get_ports { pulse_red }];
# Buttons
set_property -dict { PACKAGE_PIN D9 IOSTANDARD LVCMOS33 } [get_ports { btn[0] }];
set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { btn[1] }];
set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }];
set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }];
# CLK constraint
create_clock -period 10.0 [get_ports {clk}]

View File

@ -0,0 +1,32 @@
module top (
input wire clk,
input wire [3:0] sw,
input wire [3:0] btn,
output wire pulse_red,
pulse_blue,
pulse_green
);
wire [13:0] pulse_wideR, pulse_wideB, pulse_wideG;
assign pulse_wideR = {1'b0, sw[3:1], 10'd0};
assign pulse_wideG = {1'b0, sw[0], btn[3:2], 10'd0};
assign pulse_wideB = {btn[1:0], 11'd0};
PWM R0 (
.clk (clk),
.pulse(pulse_red),
.width(pulse_wideR)
);
PWM B0 (
.clk (clk),
.pulse(pulse_green),
.width(pulse_wideB)
);
PWM G0 (
.clk (clk),
.pulse(pulse_blue),
.width(pulse_wideG)
);
endmodule

8
xc7/timer/Makefile Normal file
View File

@ -0,0 +1,8 @@
current_dir := ${CURDIR}
TARGET := basys3
TOP := top
SOURCES := ${current_dir}/*.sv
XDC := ${current_dir}/basys3.xdc
include ${current_dir}/../../common/Makefile

31
xc7/timer/README.rst Normal file
View File

@ -0,0 +1,31 @@
Timer
~~~~~~
This example is built specifically for the basys3 and demonstrates a greater variety of I/O
then previous designs. It also demonstrates symbiflow's support for code written in System Verilog
as well as its support of dictionaries in XDCs. To build this example run the following commands:
.. code-block:: bash
:name: example-watch-basys3
make -C timer
At completion, the bitstream is located in the build directory:
.. code-block:: bash
cd timer/build/basys3
Now, you can upload the design with:
.. code-block:: bash
openocd -f ${INSTALL_DIR}/${FPGA_FAM}/conda/envs/${FPGA_FAM}/share/openocd/scripts/board/digilent_arty.cfg -c "init; pld load 0 top.bit; exit"
After downloading the bitstream you can start and stop the watch by toggling switch 0 on the board.
Press the center button to reset the counter. The following gives a visual example:
.. image:: ../../docs/images/timer.gif
:align: center
:width: 50%

26
xc7/timer/basys3.xdc Normal file
View File

@ -0,0 +1,26 @@
# Clock
set_property -dict { PACKAGE_PIN W5 IOSTANDARD LVCMOS33 } [get_ports { clk }];
create_clock -period 10.00 [get_ports {clk}];
# Buttons
set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { btnc }];
# Switches
set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { sw }];
# Seven Segment Display
set_property -dict { PACKAGE_PIN W7 IOSTANDARD LVCMOS33 } [get_ports { segment[0] }];
set_property -dict { PACKAGE_PIN W6 IOSTANDARD LVCMOS33 } [get_ports { segment[1] }];
set_property -dict { PACKAGE_PIN U8 IOSTANDARD LVCMOS33 } [get_ports { segment[2] }];
set_property -dict { PACKAGE_PIN V8 IOSTANDARD LVCMOS33 } [get_ports { segment[3] }];
set_property -dict { PACKAGE_PIN U5 IOSTANDARD LVCMOS33 } [get_ports { segment[4] }];
set_property -dict { PACKAGE_PIN V5 IOSTANDARD LVCMOS33 } [get_ports { segment[5] }];
set_property -dict { PACKAGE_PIN U7 IOSTANDARD LVCMOS33 } [get_ports { segment[6] }];
set_property -dict { PACKAGE_PIN V7 IOSTANDARD LVCMOS33 } [get_ports { segment[7] }];
set_property -dict { PACKAGE_PIN U2 IOSTANDARD LVCMOS33 } [get_ports { anode[0] }];
set_property -dict { PACKAGE_PIN U4 IOSTANDARD LVCMOS33 } [get_ports { anode[1] }];
set_property -dict { PACKAGE_PIN V4 IOSTANDARD LVCMOS33 } [get_ports { anode[2] }];
set_property -dict { PACKAGE_PIN W4 IOSTANDARD LVCMOS33 } [get_ports { anode[3] }];

31
xc7/timer/clock.sv Normal file
View File

@ -0,0 +1,31 @@
`timescale 1ns / 1ps `default_nettype none
module top (
input wire logic clk,
btnc,
sw,
output logic [3:0] anode,
output logic [7:0] segment
);
logic [15:0] digitData;
timer TC0 (
.clk(clk),
.reset(btnc),
.run(sw),
.digit0(digitData[3:0]),
.digit1(digitData[7:4]),
.digit2(digitData[11:8]),
.digit3(digitData[15:12])
);
display_control SSC0 (
.clk(clk),
.reset(btnc),
.dataIn(digitData),
.digitDisplay(4'b1111),
.digitPoint(4'b0100),
.anode(anode),
.segment(segment)
);
endmodule

View File

@ -0,0 +1,68 @@
`default_nettype none
module display_control (
input wire logic clk,
input wire logic reset,
input wire logic [15:0] dataIn,
input wire logic [ 3:0] digitDisplay,
input wire logic [ 3:0] digitPoint,
output logic [ 3:0] anode,
output logic [ 7:0] segment
);
parameter integer COUNT_BITS = 17;
logic [COUNT_BITS-1:0] count_val;
logic [ 1:0] anode_select;
logic [ 3:0] cur_anode;
logic [ 3:0] cur_data_in;
always_ff @(posedge clk) begin
if (reset) count_val <= 0;
else count_val <= count_val + 1;
end
assign anode_select = count_val[COUNT_BITS-1:COUNT_BITS-2];
assign cur_anode =
(anode_select == 2'b00) ? 4'b1110 :
(anode_select == 2'b01) ? 4'b1101 :
(anode_select == 2'b10) ? 4'b1011 :
4'b0111;
assign anode = cur_anode | (~digitDisplay);
assign cur_data_in =
(anode_select == 2'b00) ? dataIn[3:0] :
(anode_select == 2'b01) ? dataIn[7:4] :
(anode_select == 2'b10) ? dataIn[11:8] :
dataIn[15:12] ;
assign segment[7] =
(anode_select == 2'b00) ? ~digitPoint[0] :
(anode_select == 2'b01) ? ~digitPoint[1] :
(anode_select == 2'b10) ? ~digitPoint[2] :
~digitPoint[3] ;
assign segment[6:0] =
(cur_data_in == 0) ? 7'b1000000 :
(cur_data_in == 1) ? 7'b1111001 :
(cur_data_in == 2) ? 7'b0100100 :
(cur_data_in == 3) ? 7'b0110000 :
(cur_data_in == 4) ? 7'b0011001 :
(cur_data_in == 5) ? 7'b0010010 :
(cur_data_in == 6) ? 7'b0000010 :
(cur_data_in == 7) ? 7'b1111000 :
(cur_data_in == 8) ? 7'b0000000 :
(cur_data_in == 9) ? 7'b0010000 :
(cur_data_in == 10) ? 7'b0001000 :
(cur_data_in == 11) ? 7'b0000011 :
(cur_data_in == 12) ? 7'b1000110 :
(cur_data_in == 13) ? 7'b0100001 :
(cur_data_in == 14) ? 7'b0000110 :
7'b0001110;
endmodule

26
xc7/timer/modify_count.sv Normal file
View File

@ -0,0 +1,26 @@
`default_nettype none
module modify_count #(
parameter MOD_VALUE = 10
) (
input wire logic clk,
reset,
increment,
output logic rolling_over,
output logic [3:0] count = 0
);
always_ff @(posedge clk) begin
if (reset) count <= 4'b0000;
else if (increment) begin
if (rolling_over) count <= 4'b0000;
else count <= count + 4'b0001;
end
end
always_comb begin
if (increment && (count == MOD_VALUE - 1)) rolling_over = 1'b1;
else rolling_over = 1'b0;
end
endmodule

27
xc7/timer/time_counter.sv Normal file
View File

@ -0,0 +1,27 @@
`timescale 1ns / 1ps `default_nettype none
module time_counter #(
parameter MOD_VALUE = 1000000
) (
input wire logic clk,
reset,
increment,
output logic rolling_over,
output logic [23:0] count = 0
);
always_ff @(posedge clk) begin
if (reset) count <= 0;
else if (increment) begin
if (rolling_over) count <= 0;
else count <= count + 1'b1;
end
end
always_comb begin
if (increment && (count == MOD_VALUE - 1)) rolling_over = 1'b1;
else rolling_over = 1'b0;
end
endmodule

63
xc7/timer/timer.sv Normal file
View File

@ -0,0 +1,63 @@
`timescale 1ns / 1ps `default_nettype none
module timer (
input wire logic clk,
reset,
run,
output logic [3:0] digit0,
digit1,
digit2,
digit3
);
logic inc0, inc1, inc2, inc3, inc4;
logic [23:0] timerCount;
modify_count #(
.MOD_VALUE(10)
) M0 (
.clk(clk),
.reset(reset),
.increment(inc0),
.rolling_over(inc1),
.count(digit0)
);
modify_count #(
.MOD_VALUE(10)
) M1 (
.clk(clk),
.reset(reset),
.increment(inc1),
.rolling_over(inc2),
.count(digit1)
);
modify_count #(
.MOD_VALUE(10)
) M2 (
.clk(clk),
.reset(reset),
.increment(inc2),
.rolling_over(inc3),
.count(digit2)
);
modify_count #(
.MOD_VALUE(6)
) M3 (
.clk(clk),
.reset(reset),
.increment(inc3),
.rolling_over(inc4),
.count(digit3)
);
time_counter #(
.MOD_VALUE(1000000)
) T0 (
.clk(clk),
.reset(reset),
.increment(run),
.rolling_over(inc0),
.count(timerCount)
);
endmodule