Skip to content

Commit

Permalink
add vhdl/blink
Browse files Browse the repository at this point in the history
  • Loading branch information
umarcor committed Oct 12, 2020
1 parent 2776953 commit 21bcde2
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 27 deletions.
10 changes: 10 additions & 0 deletions .github/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ echo "TOOLCHAIN_PATH: $TOOLCHAIN_PATH"

export PATH=$TOOLCHAIN_PATH/bin:$PATH

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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ The contents of this workshop is published at [workshop.fomu.im](https://worksho
CPU section of the workshop](https://workshop.fomu.im/en/latest/riscv.html).
- [verilog](./verilog) - The files required for the [Verilog on Fomu section
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).

# Development

Expand Down
32 changes: 31 additions & 1 deletion docs/hdl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,40 @@ The two most common **H**\ ardware **D**\ escription **L**\ anguages are
Verilog and VHDL.

.. NOTE:: The pre-built toolchain we are releasing supports Verilog only.
However, `GHDL <https://github.com/ghdl>`_ might be used as a VHDL frontend
However, `GHDL <https://github.com/ghdl>`_ can be usable as a VHDL frontend
for Yosys. See `ghdl-yosys-plugin <https://github.com/ghdl/ghdl-yosys-plugin>`_.

When writing HDL, a tool called ``yosys`` is used to convert the
human readable verilog into a netlist representation, this is called
*synthesis*. Once we have the netlist representation, a tool called
``nextpnr`` performs an operation called *place and route* (P&R) which
makes it something that will actually run on the FPGA. This is all
done for you using the ``Makefiles`` in the subdirectories of ``verilog``
or ``vhdl``.

A big feature of ``nextpnr`` over its predecessor, is the fact that
it is timing-driven. This means that a design will be generated with
a given clock domain guaranteed to perform fast enough.

When the ``make`` command runs ``nextpnr-ice40`` you will see something
similar included in the output:

::

Info: Max frequency for clock 'clk': 73.26 MHz (PASS at 12.00 MHz)

This output example shows that we could run ``clk`` at up to 73.26
MHz and it would still be stable (even though we only requested 12.00
MHz). Note that there is some variation between designs depending on
how the placer and router decided to lay things out, so your exact
frequency numbers might be different from the ones shown in the code
blocks of this documentation.

Languages and generators
========================

.. toctree::

verilog
vhdl
migen
27 changes: 1 addition & 26 deletions docs/verilog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ number so you get an on/off pattern.

Enter the ``verilog/blink`` directory and build the demo by using ``make``:

**Make sure you set the ``FOMU_REV`` value to match your hardware! See
the Required Hardware section.**
**Make sure you set the** ``FOMU_REV`` **value to match your hardware!** See :ref:`required-hardware`.

.. session:: shell-session

Expand Down Expand Up @@ -62,30 +61,6 @@ You can then load ``blink.dfu`` onto Fomu by using 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.

When writing HDL, a tool called ``yosys`` is used to convert the
human readable verilog into a netlist representation, this is called
synthesis. Once we have the netlist representation a tool called
``nextpnr`` performs an operation called “place and route” which
makes it something that will actually run on the FPGA. This is all
done for you using the ``Makefile`` in the ``verilog/blink``
directory.

A big feature of ``nextpnr`` over its predecessor, is the fact that
it is timing-driven. This means that a design will be generated with
a given clock domain guaranteed to perform fast enough.

When the ``make`` command runs ``nextpnr-ice40`` you will see something
similar included in the output:

::

Info: Max frequency for clock 'clk': 73.26 MHz (PASS at 12.00 MHz)

This output example shows that we could run ``clk`` at up to 73.26
MHz and it would still be stable, even though we only requested 12.00
MHz. Note that there is some variation between designs depending on
how the placer and router decided to lay things out, so your exact
frequency numbers might be different.

Reading Input
^^^^^^^^^^^^^
Expand Down
67 changes: 67 additions & 0 deletions docs/vhdl.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
VHDL on Fomu
------------

.. HINT:: Component declarations for instantiating hard cores (such as the
ones in ``sb_ice40_components.vhd``) are found in the installation of
`iCEcube2 <http://www.latticesemi.com/iCEcube2>`_.


“Hello world!” - Blink a LED
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The canonical “Hello, world!” of hardware is to blink a LED. The
directory ``vhdl/blink`` contains a VHDL 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 ``vhdl/blink`` directory and build the demo by using ``make``:

**Make sure you set the** ``FOMU_REV`` **value to match your hardware!** See :ref:`required-hardware`.

.. session:: shell-session

$ make FOMU_REV=$FOMU_REV
...
Info: Max frequency for clock 'clk': 70.39 MHz (PASS at 12.00 MHz)

Info: Max delay posedge clk -> <async>: 3.15 ns

Info: Slack histogram:
Info: legend: * represents 1 endpoint(s)
Info: + represents [1,1) endpoint(s)
Info: [ 69127, 69680) |**
Info: [ 69680, 70233) |**
Info: [ 70233, 70786) |
Info: [ 70786, 71339) |**
Info: [ 71339, 71892) |**
Info: [ 71892, 72445) |**
Info: [ 72445, 72998) |**
Info: [ 72998, 73551) |
Info: [ 73551, 74104) |**
Info: [ 74104, 74657) |**
Info: [ 74657, 75210) |**
Info: [ 75210, 75763) |**
Info: [ 75763, 76316) |
Info: [ 76316, 76869) |**
Info: [ 76869, 77422) |**
Info: [ 77422, 77975) |**
Info: [ 77975, 78528) |
Info: [ 78528, 79081) |***************************
Info: [ 79081, 79634) |**
Info: [ 79634, 80187) |***
22 warnings, 0 errors
docker run --rm -v //t/fomu/fomu-workshop/vhdl/blink/../..://src -w //src/vhdl/blink ghdl/synth:icestorm 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.
76 changes: 76 additions & 0 deletions vhdl/blink/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 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_SYN_FILES = ../sb_ice40_components.vhd blink.vhd

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)
$(YOSYS) $(YOSYSFLAGS) \
-p \
"$(GHDLSYNTH) $(GHDL_FLAGS) $^ -e; \
synth_ice40 \
-top Fomu_Blink \
-json $@" 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
10 changes: 10 additions & 0 deletions vhdl/blink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Minimal VHDL Example

A minimal VHDL example which simply blinks the RGB LEDs at different
frequencies.

## 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.
80 changes: 80 additions & 0 deletions vhdl/blink/blink.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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 counter: unsigned(27 downto 0) := (others=>'0');

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';

-- Connect to system clock (with buffering)
clk_gb: SB_GB
port map (
USER_SIGNAL_TO_GLOBAL_BUFFER => clki,
GLOBAL_BUFFER_OUTPUT => clk
);

-- Use counter logic to divide system clock. The clock is 48 MHz,
-- so we divide it down by 2^28.
process(clk)
begin
if rising_edge(clk) then
counter <= counter + 1;
end if;
end process;

-- 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 => counter(counter'left),
RGB1PWM => counter(counter'left-1),
RGB2PWM => counter(counter'left-2),
RGB0 => rgb0,
RGB1 => rgb1,
RGB2 => rgb2
);

end;
32 changes: 32 additions & 0 deletions vhdl/sb_ice40_components.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
library ieee ;
use ieee.std_logic_1164.all;

package components is

component SB_GB
port(
GLOBAL_BUFFER_OUTPUT : out std_logic;
USER_SIGNAL_TO_GLOBAL_BUFFER : in std_logic
);
end component;

component SB_RGBA_DRV
generic (
CURRENT_MODE : string := "0b0";
RGB0_CURRENT : string := "0b000000";
RGB1_CURRENT : string := "0b000000";
RGB2_CURRENT : string := "0b000000"
);
port (
RGB0PWM : in std_logic;
RGB1PWM : in std_logic;
RGB2PWM : in std_logic;
CURREN : in std_logic;
RGBLEDEN : in std_logic;
RGB0 : out std_logic;
RGB1 : out std_logic;
RGB2 : out std_logic
);
end component;

end components;

0 comments on commit 21bcde2

Please sign in to comment.