diff --git a/.github/scripts/build-examples.sh b/.github/scripts/build-examples.sh index 481da14..49cfec4 100755 --- a/.github/scripts/build-examples.sh +++ b/.github/scripts/build-examples.sh @@ -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 diff --git a/.github/workflows/sphinx-tuttest.yml b/.github/workflows/sphinx-tuttest.yml index 759ebd2..10532e7 100644 --- a/.github/workflows/sphinx-tuttest.yml +++ b/.github/workflows/sphinx-tuttest.yml @@ -66,6 +66,24 @@ jobs: - {fpga-fam: "xc7", os: "debian", os-version: "buster", example: "button_controller"} - {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" diff --git a/docs/building-examples.rst b/docs/building-examples.rst index 8aed414..297df99 100644 --- a/docs/building-examples.rst +++ b/docs/building-examples.rst @@ -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 ----------------- diff --git a/docs/master_makefile/Makefile b/docs/master_makefile/Makefile index 12ffd7d..174b0ae 100644 --- a/docs/master_makefile/Makefile +++ b/docs/master_makefile/Makefile @@ -63,4 +63,4 @@ ${BOARD_BUILDDIR}/${TOP}.bit: ${BOARD_BUILDDIR}/${TOP}.fasm cd ${BOARD_BUILDDIR} && symbiflow_write_bitstream -d ${BITSTREAM_DEVICE} -f ${TOP}.fasm -p ${PARTNAME} -b ${TOP}.bit clean: - rm -rf ${BUILDDIR} + rm -rf ${BUILDDIR} \ No newline at end of file diff --git a/xc7/additional_examples/button_controller/display_control.sv b/xc7/additional_examples/button_controller/display_control.sv index 1f71c59..f320eb5 100644 --- a/xc7/additional_examples/button_controller/display_control.sv +++ b/xc7/additional_examples/button_controller/display_control.sv @@ -30,7 +30,6 @@ module display_control ( (anode_select == 2'b10) ? 4'b1011 : 4'b0111; - assign anode = cur_anode | (~digitDisplay); assign cur_data_in = diff --git a/xc7/pulse_width_led/Makefile b/xc7/pulse_width_led/Makefile new file mode 100644 index 0000000..d4069df --- /dev/null +++ b/xc7/pulse_width_led/Makefile @@ -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 \ No newline at end of file diff --git a/xc7/pulse_width_led/PWM.v b/xc7/pulse_width_led/PWM.v new file mode 100644 index 0000000..74f33e1 --- /dev/null +++ b/xc7/pulse_width_led/PWM.v @@ -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 diff --git a/xc7/pulse_width_led/README.rst b/xc7/pulse_width_led/README.rst new file mode 100644 index 0000000..ee39c2c --- /dev/null +++ b/xc7/pulse_width_led/README.rst @@ -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% + + diff --git a/xc7/pulse_width_led/arty_35.xdc b/xc7/pulse_width_led/arty_35.xdc new file mode 100644 index 0000000..2758008 --- /dev/null +++ b/xc7/pulse_width_led/arty_35.xdc @@ -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}] \ No newline at end of file diff --git a/xc7/pulse_width_led/pulse_led.v b/xc7/pulse_width_led/pulse_led.v new file mode 100644 index 0000000..ec8fb37 --- /dev/null +++ b/xc7/pulse_width_led/pulse_led.v @@ -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 diff --git a/xc7/timer/Makefile b/xc7/timer/Makefile new file mode 100644 index 0000000..e022307 --- /dev/null +++ b/xc7/timer/Makefile @@ -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 \ No newline at end of file diff --git a/xc7/timer/README.rst b/xc7/timer/README.rst new file mode 100644 index 0000000..3e81195 --- /dev/null +++ b/xc7/timer/README.rst @@ -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% \ No newline at end of file diff --git a/xc7/timer/basys3.xdc b/xc7/timer/basys3.xdc new file mode 100644 index 0000000..b415f9a --- /dev/null +++ b/xc7/timer/basys3.xdc @@ -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] }]; \ No newline at end of file diff --git a/xc7/timer/clock.sv b/xc7/timer/clock.sv new file mode 100644 index 0000000..3ab6b7d --- /dev/null +++ b/xc7/timer/clock.sv @@ -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 diff --git a/xc7/timer/display_control.sv b/xc7/timer/display_control.sv new file mode 100644 index 0000000..ab0d7a5 --- /dev/null +++ b/xc7/timer/display_control.sv @@ -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 diff --git a/xc7/timer/modify_count.sv b/xc7/timer/modify_count.sv new file mode 100644 index 0000000..be7ee3f --- /dev/null +++ b/xc7/timer/modify_count.sv @@ -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 diff --git a/xc7/timer/time_counter.sv b/xc7/timer/time_counter.sv new file mode 100644 index 0000000..e6cbc20 --- /dev/null +++ b/xc7/timer/time_counter.sv @@ -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 diff --git a/xc7/timer/timer.sv b/xc7/timer/timer.sv new file mode 100644 index 0000000..3aa608f --- /dev/null +++ b/xc7/timer/timer.sv @@ -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