diff options
Diffstat (limited to 'machxo2/examples')
-rw-r--r-- | machxo2/examples/.gitignore | 11 | ||||
-rw-r--r-- | machxo2/examples/README.md | 110 | ||||
-rw-r--r-- | machxo2/examples/blinky.v | 17 | ||||
-rw-r--r-- | machxo2/examples/blinky_ext.v | 19 | ||||
-rw-r--r-- | machxo2/examples/blinky_tb.v | 38 | ||||
-rw-r--r-- | machxo2/examples/demo.sh | 22 | ||||
-rw-r--r-- | machxo2/examples/mitertest.sh | 85 | ||||
-rw-r--r-- | machxo2/examples/rgbcount.v | 33 | ||||
-rw-r--r-- | machxo2/examples/simple.sh | 34 | ||||
-rw-r--r-- | machxo2/examples/simtest.sh | 39 | ||||
-rw-r--r-- | machxo2/examples/tinyfpga.v | 28 | ||||
-rw-r--r-- | machxo2/examples/uart.v | 209 |
12 files changed, 645 insertions, 0 deletions
diff --git a/machxo2/examples/.gitignore b/machxo2/examples/.gitignore new file mode 100644 index 00000000..8a87cc8d --- /dev/null +++ b/machxo2/examples/.gitignore @@ -0,0 +1,11 @@ +*_simtest* +*.vcd +*.png +*.log +*.smt2 +pack*.v +place*.v +pnr*.v +abc.history +*.txt +*.bit diff --git a/machxo2/examples/README.md b/machxo2/examples/README.md new file mode 100644 index 00000000..3542da70 --- /dev/null +++ b/machxo2/examples/README.md @@ -0,0 +1,110 @@ +# MachXO2 Architecture Example +This directory contains a simple example of running `nextpnr-machxo2`: + +* `simple.sh` produces nextpnr output in the files `{pack,place,pnr}*.json`, + as well as pre-pnr and post-pnr diagrams in `{pack,place,pnr}*.{dot, png}`. +* `simtest.sh` extends `simple.sh` by generating `{pack,place,pnr}*.v` from + `{pack,place,pnr}*.json`. The script calls the [`iverilog`](http://iverilog.icarus.com) + compiler and `vvp` runtime to compare the behavior of `{pack,place,pnr}*.v` + and the original Verilog input (using a testbench `*_tb.v`). This is known as + post-place-and-route simulation. +* `mitertest.sh` is similar to `simtest.sh`, but more comprehensive. This + script creates a [miter circuit](https://www21.in.tum.de/~lammich/2015_SS_Seminar_SAT/resources/Equivalence_Checking_11_30_08.pdf) + to compare the output port values of `{pack,place,pnr}*.v` against the + original Verilog code _when both modules are fed the same values on their input + ports._ + + All possible inputs and resulting outputs can be tested in reasonable time by + using `yosys`' built-in SAT solver or [`z3`](https://github.com/Z3Prover/z3), + an external SMT solver. +* `demo.sh` creates bitstreams for [TinyFPGA Ax](https://tinyfpga.com/a-series-guide.html) + and writes the resulting bitstream to MachXO2's internal flash using + [`tinyproga`](https://github.com/tinyfpga/TinyFPGA-A-Programmer). + +As `nextpnr-machxo2` is developed the contents `simple.sh`, `simtest.sh`, +`mitertest.sh`, and `demo.sh` are subject to change. + +## How To Run +Each script requires a prefix that matches one of the self-contained Verilog +examples in this directory. For instance, to create a bitstream from +`tinyfpga.v`, use `demo.sh tinyfpga` (the `*` glob used throughout this file +is filled with the the prefix). + +Each of `simple.sh`, `simtest.sh`, and `mitertest.sh` runs yosys and nextpnr +to validate a Verilog design in various ways. They require an additional `mode` +argument- `pack`, `place`, or `pnr`- which stops `nextpnr-machxo2` after the +specified phase and writes out a JSON file of the results in +`{pack,place,pnr}*.json`; `pnr` runs all of the Pack, Place, and Route phases. + +`mitertest.sh` requires an third option- `sat` or `smt`- to choose between +verifying the miter with either yosys' built-in SAT solver, or an external +SMT solver. + +Each script will exit if it finds an input Verilog example it knows it can't +handle. To keep file count lower, all yosys scripts are written inline inside +the `sh` scripts using the `-p` option. + +### Clean +To clean output files from _all_ scripts, run: + +``` +rm -rf *.dot *.json *.png *.vcd *.smt2 *.log *.txt *.bit {pack,place,pnr}*.v *_simtest* +``` + +## Known Issues +In principle, `mitertest.sh` should work in `sat` or `smt` mode with all +example Verilog files which don't use the internal oscillator (OSCH) or other +hard IP. However, as of this writing, only `blinky.v` passes correctly for a +few reasons: + + 1. The sim models for MachXO2 primitives used by the `gate` module contain + `initial` values _by design_, as it matches chip behavior. Without any of + the following in the `gold` module (like `blinky_ext.v` currently): + + * An external reset signal + * Internal power-on reset signal (e.g. `reg int_rst = 1'd1;`) + * `initial` values to manually set registers + + the `gold` and `gate` modules will inherently not match. + + Examples using an internal power-on reset (e.g. `uart.v`) also have issues + that I haven't debugged yet in both `sat` and `smt` mode. + 2. To keep the `gold`/`gate` generation simpler, examples are currently + assumed to _not_ instantiate MachXO2 simulation primitives directly + (`FACADE_IO`, `FACADE_FF`, etc). + 3. `synth_machxo2` runs `deminout` on `inouts` when generating the `gate` + module. This is not handled yet when generating the `gold` module. + +## Verilog Examples +* `blinky.v`/`blinky_tb.v`- A blinky example meant for simulation. +* `tinyfpga.v`- Blink the LED on TinyFPA Ax. +* `rgbcount.v`- Blink an RGB LED using TinyFPGA Ax, more closely-based on + [the TinyFPGA Ax guide](https://tinyfpga.com/a-series-guide.html). +* `blinky_ext.v`- Blink the LED on TinyFPA Ax using an external pin (pin 6). +* `uart.v`- UART loopback demo at 19200 baud. Requires the following pins: + + * Pin 1- RX LED + * Pin 2- TX (will echo RX) + * Pin 3- RX + * Pin 4- TX LED + * Pin 5- Load LED + * Pin 6- 12 MHz clock input + * Pin 7- Take LED + * Pin 8- Empty LED + +## Environment Variables For Scripts +* `YOSYS`- Set to the location of the `yosys` binary to test. Defaults to the + `yosys` on the path. You may want to set this to a `yosys` binary in your + source tree if doing development. +* `NEXTPNR`- Set to the location of the `nextpnr-machxo2` binary to test. + Defaults to the `nextpnr-machxo2` binary at the root of the `nextpnr` source + tree. This should be set, for instance, if doing an out-of-tree build of + `nextpnr-machxo2`. +* `CELLS_SIM`- Set to the location of `machxo2/cells_sim.v` simulation models. + Defaults to whatever `yosys-config` associated with the above `YOSYS` binary + returns. You may want to set this to `/path/to/yosys/src/share/machxo2/cells_sim.v` + if doing development; `yosys-config` cannot find these "before-installation" + simulation models. +* `TRELLIS_DB`- Set to the location of the Project Trellis database to use. + Defaults to nothing, which means `ecppack` will use whatever database is on + its path. diff --git a/machxo2/examples/blinky.v b/machxo2/examples/blinky.v new file mode 100644 index 00000000..57bad543 --- /dev/null +++ b/machxo2/examples/blinky.v @@ -0,0 +1,17 @@ +module top(input clk, rst, output [7:0] leds); + +// TODO: Test miter circuit without reset value. SAT and SMT diverge without +// reset value (SAT succeeds, SMT fails). I haven't figured out the correct +// init set of options to make SAT fail. +// "sat -verify -prove-asserts -set-init-def -seq 1 miter" causes assertion +// failure in yosys. +reg [7:0] ctr = 8'h00; +always @(posedge clk) + if (rst) + ctr <= 8'h00; + else + ctr <= ctr + 1'b1; + +assign leds = ctr; + +endmodule diff --git a/machxo2/examples/blinky_ext.v b/machxo2/examples/blinky_ext.v new file mode 100644 index 00000000..a8bdd588 --- /dev/null +++ b/machxo2/examples/blinky_ext.v @@ -0,0 +1,19 @@ +// Modified from: +// https://github.com/tinyfpga/TinyFPGA-A-Series/tree/master/template_a2 + +module top ( + (* LOC="13" *) + output pin1, + (* LOC="21" *) + input clk +); + + reg [23:0] led_timer; + + always @(posedge clk) begin + led_timer <= led_timer + 1; + end + + // left side of board + assign pin1 = led_timer[23]; +endmodule diff --git a/machxo2/examples/blinky_tb.v b/machxo2/examples/blinky_tb.v new file mode 100644 index 00000000..f9925e6f --- /dev/null +++ b/machxo2/examples/blinky_tb.v @@ -0,0 +1,38 @@ +`timescale 1ns / 1ps +module blinky_tb; + +reg clk = 1'b0, rst = 1'b0; +reg [7:0] ctr_gold = 8'h00; +wire [7:0] ctr_gate; +top dut_i(.clk(clk), .rst(rst), .leds(ctr_gate)); + +task oneclk; + begin + clk = 1'b1; + #10; + clk = 1'b0; + #10; + end +endtask + +initial begin + $dumpfile("blinky_simtest.vcd"); + $dumpvars(0, blinky_tb); + #100; + rst = 1'b1; + repeat (5) oneclk; + #5 + rst = 1'b0; + #5 + repeat (500) begin + if (ctr_gold !== ctr_gate) begin + $display("mismatch gold=%b gate=%b", ctr_gold, ctr_gate); + $stop; + end + oneclk; + ctr_gold = ctr_gold + 1'b1; + end + $finish; +end + +endmodule diff --git a/machxo2/examples/demo.sh b/machxo2/examples/demo.sh new file mode 100644 index 00000000..00cb0cd0 --- /dev/null +++ b/machxo2/examples/demo.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +if [ $# -lt 1 ]; then + echo "Usage: $0 prefix" + exit -1 +fi + +if ! grep -q "(\*.*LOC.*\*)" $1.v; then + echo "$1.v does not have LOC constraints for tinyfpga_a." + exit -2 +fi + +if [ ! -z ${TRELLIS_DB+x} ]; then + DB_ARG="--db $TRELLIS_DB" +fi + +set -ex + +${YOSYS:-yosys} -p "synth_machxo2 -json $1.json" $1.v +${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --no-iobs --json $1.json --textcfg $1.txt +ecppack --compress $DB_ARG $1.txt $1.bit +tinyproga -b $1.bit diff --git a/machxo2/examples/mitertest.sh b/machxo2/examples/mitertest.sh new file mode 100644 index 00000000..cfae28b7 --- /dev/null +++ b/machxo2/examples/mitertest.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "Usage: $0 prefix nextpnr_mode solve_mode" + exit -1 +fi + +if grep -q "OSCH" $1.v; then + echo "$1.v uses blackbox primitive OSCH and cannot be simulated." + exit -2 +fi + +case $2 in + "pack") + NEXTPNR_MODE="--pack-only" + ;; + "place") + NEXTPNR_MODE="--no-route" + ;; + "pnr") + NEXTPNR_MODE="" + ;; + *) + echo "nextpnr_mode string must be \"pack\", \"place\", or \"pnr\"" + exit -3 + ;; +esac + +case $3 in + "sat") + SAT=1 + ;; + "smt") + SMT=1 + ;; + *) + echo "solve_mode string must be \"sat\", or \"smt\"" + exit -4 + ;; +esac + +do_sat() { + ${YOSYS:-yosys} -l ${2}${1}_miter_sat.log -p "read_verilog ${1}.v + rename top gold + read_verilog ${2}${1}.v + rename top gate + read_verilog +/machxo2/cells_sim.v + + miter -equiv -make_assert -flatten gold gate ${2}${1}_miter + hierarchy -top ${2}${1}_miter + sat -verify -prove-asserts -tempinduct ${2}${1}_miter" +} + +do_smt() { + ${YOSYS:-yosys} -l ${2}${1}_miter_smt.log -p "read_verilog ${1}.v + rename top gold + read_verilog ${2}${1}.v + rename top gate + read_verilog +/machxo2/cells_sim.v + + miter -equiv -make_assert gold gate ${2}${1}_miter + hierarchy -top ${2}${1}_miter; proc; + opt_clean + write_verilog ${2}${1}_miter.v + write_smt2 ${2}${1}_miter.smt2" + + yosys-smtbmc -s z3 --dump-vcd ${2}${1}_miter_bmc.vcd ${2}${1}_miter.smt2 + yosys-smtbmc -s z3 -i --dump-vcd ${2}${1}_miter_tmp.vcd ${2}${1}_miter.smt2 +} + +set -ex + +${YOSYS:-yosys} -p "read_verilog ${1}.v + synth_machxo2 -noiopad -json ${1}.json" +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v + read_json ${2}${1}.json + clean -purge + write_verilog -noattr -norename ${2}${1}.v" + +if [ $3 = "sat" ]; then + do_sat $1 $2 +elif [ $3 = "smt" ]; then + do_smt $1 $2 +fi diff --git a/machxo2/examples/rgbcount.v b/machxo2/examples/rgbcount.v new file mode 100644 index 00000000..bf5c7518 --- /dev/null +++ b/machxo2/examples/rgbcount.v @@ -0,0 +1,33 @@ +// Modified from: +// https://github.com/tinyfpga/TinyFPGA-A-Series/tree/master/template_a2 +// https://tinyfpga.com/a-series-guide.html used as a basis. + +module top ( + (* LOC="21" *) + inout pin6, + (* LOC="26" *) + inout pin9_jtgnb, + (* LOC="27" *) + inout pin10_sda, +); + wire clk; + + OSCH #( + .NOM_FREQ("2.08") + ) internal_oscillator_inst ( + .STDBY(1'b0), + .OSC(clk) + ); + + reg [23:0] led_timer; + + always @(posedge clk) begin + led_timer <= led_timer + 1; + end + + // left side of board + assign pin9_jtgnb = led_timer[23]; + assign pin10_sda = led_timer[22]; + assign pin6 = led_timer[21]; + +endmodule diff --git a/machxo2/examples/simple.sh b/machxo2/examples/simple.sh new file mode 100644 index 00000000..1da60933 --- /dev/null +++ b/machxo2/examples/simple.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +if [ $# -lt 2 ]; then + echo "Usage: $0 prefix mode" + exit -1 +fi + +case $2 in + "pack") + NEXTPNR_MODE="--pack-only" + ;; + "place") + NEXTPNR_MODE="--no-route" + ;; + "pnr") + NEXTPNR_MODE="" + ;; + *) + echo "Mode string must be \"pack\", \"place\", or \"pnr\"" + exit -2 + ;; +esac + +set -ex + +${YOSYS:-yosys} -p "read_verilog ${1}.v + synth_machxo2 -json ${1}.json + show -format png -prefix ${1}" +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v + read_json ${2}${1}.json + clean -purge + show -format png -prefix ${2}${1} + write_verilog -noattr -norename ${2}${1}.v" diff --git a/machxo2/examples/simtest.sh b/machxo2/examples/simtest.sh new file mode 100644 index 00000000..2c7f6f30 --- /dev/null +++ b/machxo2/examples/simtest.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +if [ $# -lt 2 ]; then + echo "Usage: $0 prefix mode" + exit -1 +fi + +case $2 in + "pack") + NEXTPNR_MODE="--pack-only" + ;; + "place") + NEXTPNR_MODE="--no-route" + ;; + "pnr") + NEXTPNR_MODE="" + ;; + *) + echo "Mode string must be \"pack\", \"place\", or \"pnr\"" + exit -2 + ;; +esac + +if [ ! -f ${1}_tb.v ]; then + echo "No testbench file (${1}_tb.v) found for ${1}.v" + exit -3 +fi + +set -ex + +${YOSYS:-yosys} -p "read_verilog ${1}.v + synth_machxo2 -json ${1}.json" +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v + read_json ${2}${1}.json + clean -purge + write_verilog -noattr -norename ${2}${1}.v" +iverilog -o ${1}_simtest ${CELLS_SIM:-`${YOSYS:yosys}-config --datdir/machxo2/cells_sim.v`} ${1}_tb.v ${2}${1}.v +vvp -N ./${1}_simtest diff --git a/machxo2/examples/tinyfpga.v b/machxo2/examples/tinyfpga.v new file mode 100644 index 00000000..bd26d8eb --- /dev/null +++ b/machxo2/examples/tinyfpga.v @@ -0,0 +1,28 @@ +// Modified from: +// https://github.com/tinyfpga/TinyFPGA-A-Series/tree/master/template_a2 +// https://tinyfpga.com/a-series-guide.html used as a basis. + +module top ( + (* LOC="13" *) + inout pin1 +); + + + wire clk; + + OSCH #( + .NOM_FREQ("16.63") + ) internal_oscillator_inst ( + .STDBY(1'b0), + .OSC(clk) + ); + + reg [23:0] led_timer; + + always @(posedge clk) begin + led_timer <= led_timer + 1; + end + + // left side of board + assign pin1 = led_timer[23]; +endmodule diff --git a/machxo2/examples/uart.v b/machxo2/examples/uart.v new file mode 100644 index 00000000..f1d95bd8 --- /dev/null +++ b/machxo2/examples/uart.v @@ -0,0 +1,209 @@ +/* Example UART derived from: https://github.com/cr1901/migen_uart. + Requires 12MHz clock and runs at 19,200 baud. */ + +/* Machine-generated using Migen */ + +module top( + (* LOC = "14" *) + output tx, + (* LOC = "16" *) + input rx, + (* LOC = "13" *) + output rx_led, + (* LOC = "17" *) + output tx_led, + (* LOC = "20" *) + output load_led, + (* LOC = "23" *) + output take_led, + (* LOC = "25" *) + output empty_led, + (* LOC = "21" *) + input clk +); + +wire [7:0] out_data; +wire [7:0] in_data; +reg wr = 1'd0; +reg rd = 1'd0; +wire tx_empty; +wire rx_empty; +wire tx_ov; +wire rx_ov; +wire sout_load; +wire [7:0] sout_out_data; +wire sout_shift; +reg sout_empty = 1'd1; +reg sout_overrun = 1'd0; +reg [3:0] sout_count = 4'd0; +reg [9:0] sout_reg = 10'd0; +reg sout_tx; +wire sin_rx; +wire sin_shift; +wire sin_take; +reg [7:0] sin_in_data = 8'd0; +wire sin_edge; +reg sin_empty = 1'd1; +reg sin_busy = 1'd0; +reg sin_overrun = 1'd0; +reg sin_sync_rx = 1'd0; +reg [8:0] sin_reg = 9'd0; +reg sin_rx_prev = 1'd0; +reg [3:0] sin_count = 4'd0; +wire out_active; +wire in_active; +reg shift_out_strobe = 1'd0; +reg shift_in_strobe = 1'd0; +reg [9:0] in_counter = 10'd0; +reg [9:0] out_counter = 10'd0; +wire sys_clk; +wire sys_rst; +wire por_clk; +reg int_rst = 1'd1; + +// synthesis translate_off +reg dummy_s; +initial dummy_s <= 1'd0; +// synthesis translate_on + +assign tx_led = (~tx); +assign rx_led = (~rx); +assign load_led = sout_load; +assign take_led = sin_take; +assign empty_led = sin_empty; +assign out_data = in_data; +assign in_data = sin_in_data; +assign sout_out_data = out_data; +assign sin_take = rd; +assign sout_load = wr; +assign tx = sout_tx; +assign sin_rx = rx; +assign tx_empty = sout_empty; +assign rx_empty = sin_empty; +assign tx_ov = sout_overrun; +assign rx_ov = sin_overrun; +assign sout_shift = shift_out_strobe; +assign sin_shift = shift_in_strobe; +assign out_active = (~sout_empty); +assign in_active = sin_busy; + +// synthesis translate_off +reg dummy_d; +// synthesis translate_on +always @(*) begin + sout_tx <= 1'd0; + if (sout_empty) begin + sout_tx <= 1'd1; + end else begin + sout_tx <= sout_reg[0]; + end +// synthesis translate_off + dummy_d <= dummy_s; +// synthesis translate_on +end +assign sin_edge = ((sin_rx_prev == 1'd1) & (sin_sync_rx == 1'd0)); +assign sys_clk = clk; +assign por_clk = clk; +assign sys_rst = int_rst; + +always @(posedge por_clk) begin + int_rst <= 1'd0; +end + +always @(posedge sys_clk) begin + wr <= 1'd0; + rd <= 1'd0; + if ((~sin_empty)) begin + wr <= 1'd1; + rd <= 1'd1; + end + if (sout_load) begin + if (sout_empty) begin + sout_reg[0] <= 1'd0; + sout_reg[8:1] <= sout_out_data; + sout_reg[9] <= 1'd1; + sout_empty <= 1'd0; + sout_overrun <= 1'd0; + sout_count <= 1'd0; + end else begin + sout_overrun <= 1'd1; + end + end + if (((~sout_empty) & sout_shift)) begin + sout_reg[8:0] <= sout_reg[9:1]; + sout_reg[9] <= 1'd0; + if ((sout_count == 4'd9)) begin + sout_empty <= 1'd1; + sout_count <= 1'd0; + end else begin + sout_count <= (sout_count + 1'd1); + end + end + sin_sync_rx <= sin_rx; + sin_rx_prev <= sin_sync_rx; + if (sin_take) begin + sin_empty <= 1'd1; + sin_overrun <= 1'd0; + end + if (((~sin_busy) & sin_edge)) begin + sin_busy <= 1'd1; + end + if ((sin_shift & sin_busy)) begin + sin_reg[8] <= sin_sync_rx; + sin_reg[7:0] <= sin_reg[8:1]; + if ((sin_count == 4'd9)) begin + sin_in_data <= sin_reg[8:1]; + sin_count <= 1'd0; + sin_busy <= 1'd0; + if ((~sin_empty)) begin + sin_overrun <= 1'd1; + end else begin + sin_empty <= 1'd0; + end + end else begin + sin_count <= (sin_count + 1'd1); + end + end + out_counter <= 1'd0; + in_counter <= 1'd0; + if (in_active) begin + shift_in_strobe <= 1'd0; + in_counter <= (in_counter + 1'd1); + if ((in_counter == 9'd311)) begin + shift_in_strobe <= 1'd1; + end + if ((in_counter == 10'd623)) begin + in_counter <= 1'd0; + end + end + if (out_active) begin + shift_out_strobe <= 1'd0; + out_counter <= (out_counter + 1'd1); + if ((out_counter == 10'd623)) begin + out_counter <= 1'd0; + shift_out_strobe <= 1'd1; + end + end + if (sys_rst) begin + wr <= 1'd0; + rd <= 1'd0; + sout_empty <= 1'd1; + sout_overrun <= 1'd0; + sout_count <= 4'd0; + sout_reg <= 10'd0; + sin_in_data <= 8'd0; + sin_empty <= 1'd1; + sin_busy <= 1'd0; + sin_overrun <= 1'd0; + sin_sync_rx <= 1'd0; + sin_reg <= 9'd0; + sin_rx_prev <= 1'd0; + sin_count <= 4'd0; + shift_out_strobe <= 1'd0; + shift_in_strobe <= 1'd0; + in_counter <= 10'd0; + out_counter <= 10'd0; + end +end + +endmodule |