diff --git a/.github/tests.sh b/.github/tests.sh index d8ca5b8a..2ce76945 100755 --- a/.github/tests.sh +++ b/.github/tests.sh @@ -8,16 +8,6 @@ echo "TOOLCHAIN_PATH: $TOOLCHAIN_PATH" export PATH=$TOOLCHAIN_PATH/bin:$PATH export GHDL_PREFIX=$TOOLCHAIN_PATH/lib/ghdl -echo '::group::VHDL Blink example' -( - - set -x - cd vhdl/blink - make FOMU_REV=pvt - file blink.dfu -) -echo '::endgroup::' - echo '::group::RISC-V C Example' ( set -x @@ -63,6 +53,26 @@ echo '::group::Verilog Blink (expanded) example for PVT board' ) echo '::endgroup::' +echo '::group::VHDL Blink example' +( + + set -x + cd vhdl/blink + make FOMU_REV=pvt + file blink.dfu +) +echo '::endgroup::' + +echo '::group::Mixed HDL Blink example' +( + + set -x + cd mixed-hdl/blink + make FOMU_REV=pvt + file blink.dfu +) +echo '::endgroup::' + echo '::group::LiteX example for Hacker' ( set -x diff --git a/README.md b/README.md index ffe5177a..6372d5e9 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ The contents of this workshop is published at [workshop.fomu.im](https://worksho of the workshop](https://workshop.fomu.im/en/latest/verilog.html). - [vhdl](./vhdl) - The files required for the [VHDL on Fomu section of the workshop](https://workshop.fomu.im/en/latest/vhdl.html). +- [mixed-hdl](./mixed-hdl) - The files required for the [Mixed HDL on Fomu section + of the workshop](https://workshop.fomu.im/en/latest/mixedhdl.html). # Development diff --git a/docs/hdl.rst b/docs/hdl.rst index 04242b7b..48872785 100644 --- a/docs/hdl.rst +++ b/docs/hdl.rst @@ -43,4 +43,5 @@ Languages and generators verilog vhdl + mixed-hdl migen diff --git a/docs/mixed-hdl.rst b/docs/mixed-hdl.rst new file mode 100644 index 00000000..56dd8b59 --- /dev/null +++ b/docs/mixed-hdl.rst @@ -0,0 +1,80 @@ +.. _HDLs:mixed: + +Mixed HDL on Fomu +----------------- + +.. HINT:: It is strongly suggested to get familiar with :ref:`HDLs:Verilog` and :ref:`HDLs:VHDL` + examples before tinkering with these mixed language use cases. + + +“Hello world!” - Blink a LED +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The canonical “Hello, world!” of hardware is to blink a LED. The +directory ``mixedhdl/blink`` contains a VHDL + Verilog example of a blink +project. This takes the 48 MHz clock and divides it down by a large +number so you get an on/off pattern. + +Enter the ``mixedhdl/blink`` directory and build the demo by using ``make``: + +.. session:: shell-session + + $ make FOMU_REV=$FOMU_REV + ... + Info: Max frequency for clock 'clk_generator.clko': 73.26 MHz (PASS at 12.00 MHz) + + Info: Max delay posedge clk_generator.clko -> : 3.15 ns + + Info: Slack histogram: + Info: legend: * represents 1 endpoint(s) + Info: + represents [1,1) endpoint(s) + Info: [ 69683, 70208) |** + Info: [ 70208, 70733) | + Info: [ 70733, 71258) |** + Info: [ 71258, 71783) |** + Info: [ 71783, 72308) |** + Info: [ 72308, 72833) |** + Info: [ 72833, 73358) | + Info: [ 73358, 73883) |** + Info: [ 73883, 74408) |* + Info: [ 74408, 74933) |** + Info: [ 74933, 75458) |** + Info: [ 75458, 75983) |* + Info: [ 75983, 76508) |* + Info: [ 76508, 77033) |** + Info: [ 77033, 77558) |** + Info: [ 77558, 78083) |* + Info: [ 78083, 78608) | + Info: [ 78608, 79133) |************************* + Info: [ 79133, 79658) |** + Info: [ 79658, 80183) |*** + 22 warnings, 0 errors + icepack blink.asc blink.bit + cp blink.bit blink.dfu + dfu-suffix -v 1209 -p 70b1 -a blink.dfu + dfu-suffix (dfu-util) 0.9 + + Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden + This program is Free Software and has ABSOLUTELY NO WARRANTY + Please report bugs to http://sourceforge.net/p/dfu-util/tickets/ + + Suffix successfully added to file + $ + +You can then load ``blink.dfu`` onto Fomu by using ``make load`` or the same +``dfu-util -D`` command we’ve been using so far. You should see a blinking pattern of +varying color on your Fomu, indicating your bitstream was successfully loaded. + +If you take a closer look at the sources in ``mixedhdl/blink``, you will find that +modules/components ``blink`` and ``clkgen`` are written both in VHDL and Verilog. +The Makefile uses ``blink.vhd`` and ``clkgen.v`` by default. However, any of the +following cases produce the same result: + +- ``blink.vhd`` + ``clkgen.v`` +- ``blink.v`` + ``clkgen.vhdl`` +- ``blink.vhd`` + ``clkgen.vhdl`` +- ``blink.v`` + ``clkgen.v`` + +You can modify variables `VHDL_SYN_FILES` and ``VERILOG_SYN_FILES`` in the Makefile +for trying other combinations. For a better understanding, it is suggested to compare +these modules with the single file solutions in :ref:`HDLs:Verilog` and :ref:`HDLs:VHDL`. diff --git a/mixed-hdl/blink/Makefile b/mixed-hdl/blink/Makefile new file mode 100644 index 00000000..316e79cf --- /dev/null +++ b/mixed-hdl/blink/Makefile @@ -0,0 +1,82 @@ +# Different Fomu hardware revisions are wired differently and thus +# require different configurations for yosys and nextpnr. +# Configuration is performed by setting the environment variable FOMU_REV accordingly. +ifeq ($(FOMU_REV),evt1) +YOSYSFLAGS?= -D EVT=1 +PNRFLAGS ?= --up5k --package sg48 --pcf ../../pcf/fomu-evt2.pcf +else ifeq ($(FOMU_REV),evt2) +YOSYSFLAGS?= -D EVT=1 +PNRFLAGS ?= --up5k --package sg48 --pcf ../../pcf/fomu-evt2.pcf +else ifeq ($(FOMU_REV),evt3) +YOSYSFLAGS?= -D EVT=1 +PNRFLAGS ?= --up5k --package sg48 --pcf ../../pcf/fomu-evt3.pcf +else ifeq ($(FOMU_REV),hacker) +YOSYSFLAGS?= -D HACKER=1 +PNRFLAGS ?= --up5k --package uwg30 --pcf ../../pcf/fomu-hacker.pcf +else ifeq ($(FOMU_REV),pvt) +YOSYSFLAGS?= -D PVT=1 +PNRFLAGS ?= --up5k --package uwg30 --pcf ../../pcf/fomu-pvt.pcf +else +$(error Unrecognized FOMU_REV value. must be "evt1", "evt2", "evt3", "pvt", or "hacker") +endif + +# VHDL top with instantiated Verilog +VHDL_SYN_FILES = ../../vhdl/sb_ice40_components.vhd blink.vhd +VERILOG_SYN_FILES = clkgen.v + +# Verilog top with instantiated VHDL +#VHDL_SYN_FILES = ../../vhdl/sb_ice40_components.vhd clkgen.vhd +#VERILOG_SYN_FILES = blink.v + +GHDL_FLAGS += --std=08 +GHDL ?= ghdl +GHDLSYNTH ?= ghdl +YOSYS ?= yosys +NEXTPNR ?= nextpnr-ice40 +ICEPACK ?= icepack + +# Default target: run all required targets to build the DFU image. +all: blink.dfu + @true + +.DEFAULT: all + +# Use *Yosys* to generate the synthesized netlist. +# This is called the **synthesis** and **tech mapping** step. +blink.json: $(VHDL_SYN_FILES) $(VERILOG_SYN_FILES) + $(YOSYS) $(YOSYSFLAGS) \ + -p \ + "$(GHDLSYNTH) $(GHDL_FLAGS) $(VHDL_SYN_FILES) -e; \ + synth_ice40 \ + -top Fomu_Blink \ + -json $@" -q $(VERILOG_SYN_FILES) 2>&1 | tee yosys-report.txt + +# Use **nextpnr** to generate the FPGA configuration. +# This is called the **place** and **route** step. +blink.asc: blink.json + $(NEXTPNR) \ + $(PNRFLAGS) \ + --json $< \ + --asc $@ + +# Use icepack to convert the FPGA configuration into a "bitstream" loadable onto the FPGA. +# This is called the bitstream generation step. +blink.bit: blink.asc + $(ICEPACK) $< $@ + +# Use dfu-suffix to generate the DFU image from the FPGA bitstream. +blink.dfu: blink.bit + cp $< $@ + dfu-suffix -v 1209 -p 70b1 -a $@ + +# Use df-util to load the DFU image onto the Fomu. +load: blink.dfu + dfu-util -D $< + +.PHONY: load + +# Cleanup the generated files. +clean: + rm -fr *.cf *.json *-report.txt *.asc *.bit *.dfu + +.PHONY: clean diff --git a/mixed-hdl/blink/README.md b/mixed-hdl/blink/README.md new file mode 100644 index 00000000..7e74fa6b --- /dev/null +++ b/mixed-hdl/blink/README.md @@ -0,0 +1,22 @@ +# Minimal Mixed HDL Example (VHDL and Verilog) + +A minimal mixed HDL example which simply blinks the RGB LEDs at different +frequencies. + +This example contains equivalent sources in VHDL and Verilog, which can be +combined freely: + +- `blink.vhd` + `clkgen.v` +- `blink.v` + `clkgen.vhdl` +- `blink.vhd` + `clkgen.vhdl` +- `blink.v` + `clkgen.v` + +All four cases produce exactly the same result, because the same design is +described regardless of the HDL language used. In the makefile, the first +case is built by default. + +## Using + +Type `make` to build the DFU image. +Type `make load` to load the DFU image onto the Fomu board. +Type `make clean` to remove all the generated files. diff --git a/mixed-hdl/blink/blink.v b/mixed-hdl/blink/blink.v new file mode 100644 index 00000000..61b6dd70 --- /dev/null +++ b/mixed-hdl/blink/blink.v @@ -0,0 +1,77 @@ +// Simple tri-colour LED blink example. + +// Correctly map pins for the iCE40UP5K SB_RGBA_DRV hard macro. +// The variables EVT, PVT and HACKER are set from the Makefile. +`ifdef EVT +`define BLUEPWM RGB0PWM +`define REDPWM RGB1PWM +`define GREENPWM RGB2PWM +`elsif HACKER +`define BLUEPWM RGB0PWM +`define GREENPWM RGB1PWM +`define REDPWM RGB2PWM +`elsif PVT +`define GREENPWM RGB0PWM +`define REDPWM RGB1PWM +`define BLUEPWM RGB2PWM +`else +`error_board_not_supported +`endif + +module Fomu_Blink ( + // 48MHz Clock input + // -------- + input clki, + // LED outputs + // -------- + output rgb0, + output rgb1, + output rgb2, + // USB Pins (which should be statically driven if not being used). + // -------- + output usb_dp, + output usb_dn, + output usb_dp_pu +); + + // Assign USB pins to "0" so as to disconnect Fomu from + // the host system. Otherwise it would try to talk to + // us over USB, which wouldn't work since we have no stack. + assign usb_dp = 1'b0; + assign usb_dn = 1'b0; + assign usb_dp_pu = 1'b0; + + wire [2:0] color; + + // Instantiate clkgen for reducing the system clock + clkgen clk_generator ( + .clk(clki), + .cnt(color) + ); + + // Instantiate iCE40 LED driver hard logic, connecting up + // counter state and LEDs. + // + // Note that it's possible to drive the LEDs directly, + // however that is not current-limited and results in + // overvolting the red LED. + // + // See also: + // https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/IK/ICE40LEDDriverUsageGuide.ashx?document_id=50668 + SB_RGBA_DRV #( + .CURRENT_MODE("0b1"), // half current + .RGB0_CURRENT("0b000011"), // 4 mA + .RGB1_CURRENT("0b000011"), // 4 mA + .RGB2_CURRENT("0b000011") // 4 mA + ) RGBA_DRIVER ( + .CURREN(1'b1), + .RGBLEDEN(1'b1), + .`BLUEPWM(color[2]), // Blue + .`REDPWM(color[1]), // Red + .`GREENPWM(color[0]), // Green + .RGB0(rgb0), + .RGB1(rgb1), + .RGB2(rgb2) + ); + +endmodule diff --git a/mixed-hdl/blink/blink.vhd b/mixed-hdl/blink/blink.vhd new file mode 100644 index 00000000..74512281 --- /dev/null +++ b/mixed-hdl/blink/blink.vhd @@ -0,0 +1,78 @@ +library ieee; +context ieee.ieee_std_context; + +use work.components.all; + +entity Fomu_Blink is + port ( + -- 48MHz Clock input + clki: in std_logic; + + -- LED outputs + rgb0: out std_logic; + rgb1: out std_logic; + rgb2: out std_logic; + + -- USB Pins (which should be statically driven if not being used) + usb_dp: out std_logic; + usb_dn: out std_logic; + usb_dp_pu: out std_logic + ); +end; + +architecture arch of Fomu_Blink is + + signal clk: std_logic; + signal color: std_logic_vector(2 downto 0) := (others=>'0'); + + component clkgen + port ( + clk: in std_logic; + cnt: out std_logic_vector(2 downto 0) + ); + end component; + +begin + + -- Assign USB pins to "0" so as to disconnect Fomu from + -- the host system. Otherwise it would try to talk to + -- us over USB, which wouldn't work since we have no stack. + usb_dp <= '0'; + usb_dn <= '0'; + usb_dp_pu <= '0'; + + -- Instantiate clkgen for reducing the system clock + clk_generator: component clkgen + port map ( + clk => clki, + cnt => color + ); + + -- Instantiate iCE40 LED driver hard logic, connecting up + -- counter state and LEDs. + -- + -- Note that it's possible to drive the LEDs directly, + -- however that is not current-limited and results in + -- overvolting the red LED. + -- + -- See also: + -- https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/IK/ICE40LEDDriverUsageGuide.ashx?document_id=50668 + rgba_driver: SB_RGBA_DRV + generic map ( + CURRENT_MODE => "0b1", -- half current + RGB0_CURRENT => "0b000011", -- 4 mA + RGB1_CURRENT => "0b000011", -- 4 mA + RGB2_CURRENT => "0b000011" -- 4 mA + ) + port map ( + CURREN => '1', + RGBLEDEN => '1', + RGB0PWM => color(2), + RGB1PWM => color(1), + RGB2PWM => color(0), + RGB0 => rgb0, + RGB1 => rgb1, + RGB2 => rgb2 + ); + +end; diff --git a/mixed-hdl/blink/clkgen.v b/mixed-hdl/blink/clkgen.v new file mode 100644 index 00000000..4025dbfe --- /dev/null +++ b/mixed-hdl/blink/clkgen.v @@ -0,0 +1,21 @@ +module clkgen ( + input wire clk, + output wire [2:0] cnt +); + + // Connect to system clock (with buffering) + wire clko; + SB_GB clk_gb ( + .USER_SIGNAL_TO_GLOBAL_BUFFER(clk), + .GLOBAL_BUFFER_OUTPUT(clko) + ); + + // Use counter logic to divide system clock. The clock is 48 MHz, + // so we divide it down by 2^28. + reg [28:0] counter = 0; + always @(posedge clko) begin + counter <= counter + 1; + end + assign cnt = counter[25:23]; + +endmodule diff --git a/mixed-hdl/blink/clkgen.vhd b/mixed-hdl/blink/clkgen.vhd new file mode 100644 index 00000000..3a6435de --- /dev/null +++ b/mixed-hdl/blink/clkgen.vhd @@ -0,0 +1,38 @@ +library ieee; +context ieee.ieee_std_context; + +use work.components.all; + +entity clkgen is + port ( + clk : in std_logic; + cnt : out std_logic_vector(2 downto 0) + ); +end; + +architecture arch of clkgen is + + signal clko: std_logic; + signal counter: unsigned(27 downto 0) := (others=>'0'); + +begin + + -- Connect to system clock (with buffering) + clk_gb: SB_GB + port map ( + USER_SIGNAL_TO_GLOBAL_BUFFER => clk, + GLOBAL_BUFFER_OUTPUT => clko + ); + + -- Use counter logic to divide system clock. The clock is 48 MHz, + -- so we divide it down by 2^28. + process(clko) + begin + if rising_edge(clko) then + counter <= counter + 1; + end if; + end process; + + cnt <= std_logic_vector(counter(counter'left downto counter'left-cnt'length+1)); + +end;