upduino blinky (testbench not working)

This commit is contained in:
2025-08-08 14:39:10 -07:00
parent 8850c4a962
commit 44d224c8a8
12 changed files with 549 additions and 0 deletions

20
blinky/Makefile Normal file
View File

@@ -0,0 +1,20 @@
all: sim synth
sim: blinky_tb.vcd
synth: blinky.bin
blinky_tb.vcd: blinky.v sim.v blinky_tb.v
iverilog -o blinky_tb.out blinky.v blinky_tb.v sim.v
./blinky_tb.out
gtkwave blinky_tb.vcd & # blinky_tb.gtkw &
blinky.bin: blinky.v upduino.pcf
yosys -p "synth_ice40 -top blinky -json blinky.json" blinky.v
nextpnr-ice40 --up5k --json blinky.json --pcf upduino.pcf --asc blinky.txt
icepack blinky.txt blinky.bin
clean:
rm -f *.bin *.txt *.blif *.out *.vcd *~ *.json
.PHONY: all clean

34
blinky/blinky.v Normal file
View File

@@ -0,0 +1,34 @@
// cribbed from https://blog.idorobots.org/entries/upduino-fpga-tutorial.html
module blinky (output wire led_blue,
output wire led_green,
output wire led_red);
wire clk;
SB_HFOSC inthosc(
.CLKHFPU(1'b1), // power up
.CLKHFEN(1'b1), // enable
.CLKHF(clk)); // drive the clk signal
localparam N=23;
reg [N:0] counter;
always @(posedge clk)
counter <= counter+1;
SB_RGBA_DRV rgb (
.RGBLEDEN (1'b1),
.RGB0PWM (counter[N]),
.RGB1PWM (counter[N-1]),
.RGB2PWM (counter[N-2]),
.CURREN (1'b1),
.RGB0 (led_blue),
.RGB1 (led_green),
.RGB2 (led_red)
);
defparam rgb.CURRENT_MODE = "0b1";
defparam rgb.RGB0_CURRENT = "0b000001";
defparam rgb.RGB1_CURRENT = "0b000001";
defparam rgb.RGB2_CURRENT = "0b000001";
endmodule

35
blinky/blinky_tb.v Normal file
View File

@@ -0,0 +1,35 @@
`timescale 10ns/1ns
module blinky_tb;
reg clk;
wire led_blue;
wire led_green;
wire led_red;
blinky dut (
.led_blue (led_blue),
.led_green (led_green),
.led_red (led_red)
);
initial clk=0;
always #2 clk=~clk;
//-- Begin test
initial begin
//-- Set the dumpfile
$dumpfile("blinky_tb.vcd");
//-- Dump everything into the dumpfile
$dumpvars(0, blinky_tb);
//-- End after 10 time units
# 100000 $finish;
end
endmodule

23
blinky/sim.v Normal file
View File

@@ -0,0 +1,23 @@
module SB_HFOSC(
input CLKHFPU,
input CLKHFEN,
output CLKHF
);
parameter CLKHF_DIV = "0b00";
endmodule
module SB_RGBA_DRV (
input RGBLEDEN,
input RGB0PWM,
input RGB1PWM,
input RGB2PWM,
input CURREN,
output RGB0,
output RGB1,
output RGB2
);
parameter CURRENT_MODE="0b1";
parameter RGB0_CURRENT="0b000001";
parameter RGB1_CURRENT="0b000001";
parameter RGB2_CURRENT="0b000001";
endmodule

30
blinky/tb_blinky.v Normal file
View File

@@ -0,0 +1,30 @@
`timescale 1ns/10ps //Adjust to suit
module tb_blinky;
wire wire led_blue ;
wire wire led_green ;
blinky uut (
.wire led_blue ( wire led_blue ),
.wire led_green ( wire led_green )
);
parameter PERIOD = 10; //adjust for your timescale
initial begin
$dumpfile("tb_output.vcd");
$dumpvars(2, tb_blinky);
clk = 1'b0;
#(PERIOD/2);
forever
#(PERIOD/2) clk = ~clk;
end
initial begin
rst=1'b0;
#(PERIOD*2) rst=~rst;
#PERIOD rst=~rst;
end
`include "user.tb_blinky.v"
endmodule

235
blinky/tbgen.py Normal file
View File

@@ -0,0 +1,235 @@
#! /usr/bin/python
# THE BEER-WARE LICENSE" (Revision 42):
# <xfguo@xfguo.org> wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return Xiongfei(Alex) Guo.
# some minor additions by Al Williams 2018-9-11
'''
Created on 2010-4-23
@author: Alex Guo
'''
import re
import sys
import argparse
class TestbenchGenerator(object):
'''
verilog test bench auto generation
'''
def __init__(self, vfile_name = None, ofile_name = None):
self.vfile_name = vfile_name
self.vfile = None
self.ofile_name = ofile_name
if(ofile_name == None):
self.ofile = sys.stdout
self.vcont = ""
self.mod_name = ""
self.pin_list = []
self.clock_name = 'clk'
self.reset_name = 'rst'
if vfile_name == None:
sys.stderr.write("ERROR: You did not provide an input file name.\n")
sys.exit(1)
else:
self.open()
self.parser()
self.open_outputfile()
def open(self, vfile_name = None):
if vfile_name != None:
self.vfile_name = vfile_name
try:
self.vfile = open(self.vfile_name, 'r')
self.vcont = self.vfile.read()
except Exception as e:
print("ERROR: Input file error.\n ERROR: %s" % e)
sys.exit(1)
def open_outputfile(self, ofile_name = None):
try:
if(ofile_name == None):
if(self.ofile_name == None):
ofname = "tb_%s.v" % self.mod_name
self.ofile = open(ofname, 'w')
print("You did not specify an output file name, using '%s'." % ofname)
else:
self.ofile = open(self.ofile_name, 'w')
print("Output file is '%s'." % self.ofile_name)
else:
self.ofile = open(ofile_name, 'w')
print("Output file is '%s'." % ofile_name)
except Exception as e:
print("ERROR: Output file error. \n ERROR: %s" % e)
sys.exit(1)
def clean_other(self, cont):
## clean '// ...'
cont = re.sub(r"//[^\n^\r]*", '\n', cont)
## clean '/* ... */'
cont = re.sub(r"/\*.*\*/", '', cont)
## clean '`define ..., etc.'
#cont = re.sub(r"[^\n^\r]+`[^\n^\r]*", '\n', cont)
## clean tables
cont = re.sub(r' +', ' ', cont)
## clean '\n' * '\r'
#cont = re.sub(r'[\n\r]+', '', cont)
return cont
def parser(self):
print("Parsing...")
# print vf_cont
mod_pattern = r"module[\s]+(\S*)[\s]*\([^\)]*\)[\s\S]*"
module_result = re.findall(mod_pattern, self.clean_other(self.vcont))
#print module_result
self.mod_name = module_result[0]
self.parser_inoutput()
self.find_clk_rst()
def parser_inoutput(self):
pin_list = self.clean_other(self.vcont)
comp_pin_list_pre = []
for i in re.findall(r'(input|output|inout)[\s]+([^;,\)]+)[\s]*[;,]', pin_list):
comp_pin_list_pre.append((i[0], re.sub(r"^reg[\s]*", "", i[1])))
comp_pin_list = []
type_name = ['reg', 'wire', 'wire', "ERROR"]
for i in comp_pin_list_pre:
x = re.split(r']', i[1])
type = 0;
if i[0] == 'input':
type = 0
elif i[0] == 'output':
type = 1
elif i[0] == 'inout':
type = 2
else:
type = 3
if len(x) == 2:
x[1] = re.sub('[\s]*', '', x[1])
comp_pin_list.append((i[0], x[1], x[0] + ']', type_name[type]))
else:
comp_pin_list.append((i[0], x[0], '', type_name[type]))
self.pin_list = comp_pin_list
# for i in self.pin_list: print i
def print_dut(self):
max_len = 0
for cpin_name in self.pin_list:
pin_name = cpin_name[1]
if len(pin_name) > max_len:
max_len = len(pin_name)
self.printo( "%s uut (\n" % self.mod_name )
align_cont = self.align_print(list(map(lambda x:("", "." + x[1], "(", x[1], '),'), self.pin_list)), 4)
align_cont = align_cont[:-2] + "\n"
self.printo( align_cont )
self.printo( ");\n" )
def print_wires(self):
self.printo(self.align_print(list(map(lambda x:(x[3], x[2], x[1], ';'), self.pin_list)), 4))
self.printo("\n")
def print_clock_gen(self,period,dfile,depth,resetpol):
fsdb = " $dumpfile(\"%s\");\n $dumpvars(%d, tb_%s);\n" % (dfile, depth, self.mod_name)
clock_gen_text = "\nparameter PERIOD = %d; //adjust for your timescale\n\ninitial begin\n%s CLK = 1'b0;\n #(PERIOD/2);\n forever\n #(PERIOD/2) CLK = ~CLK;\nend\n" % (period, fsdb)
self.printo(re.sub('CLK', self.clock_name, clock_gen_text))
if self.reset_name!="":
clock_gen_text = "\ninitial begin\n RST=1'b%d;\n #(PERIOD*2) RST=~RST;\n #PERIOD RST=~RST;\n end\n" % resetpol
self.printo(re.sub('RST',self.reset_name, clock_gen_text))
def find_clk_rst(self):
for pin in self.pin_list:
if re.match(r'[\S]*(clk|clock)[\S]*', pin[1]):
self.clock_name = pin[1]
print("Clock signal detected: '%s'." % pin[1])
break
for pin in self.pin_list:
if re.match(r'rst|reset', pin[1]):
self.reset_name = pin[1]
print("Reset signal detected: '%s'." % pin[1])
break
# Original code -- no longer used
def print_module_head_orig(self):
self.printo("`include \"timescale.v\"\nmodule tb_%s;\n\n" % self.mod_name)
def print_module_head(self,timescale):
self.printo("`timescale %s //Adjust to suit\n\nmodule tb_%s;\n\n" % (timescale,self.mod_name))
def print_module_end(self,iname):
if iname is None:
iname='user.tb_%s.v' % self.mod_name
self.printo("`include \"%s\"\nendmodule\n" % iname)
def printo(self, cont):
self.ofile.write(cont)
def close(self):
if self.vfile != None:
self.vfile.close()
print("Output complete.\n\n")
def align_print(self, content, indent):
""" Align pretty print."""
row_len = len(content)
col_len = len(content[0])
align_cont = [""] * row_len
for i in range(col_len):
col = list(map(lambda x:x[i], content))
max_len = max(list(map(len, col)))
for i in range(row_len):
l = len(col[i])
align_cont[i] += "%s%s" % (col[i], (indent + max_len - l) * ' ')
# remove space in line end
align_cont = list(map(lambda s:re.sub('[ ]*$', '', s), align_cont))
return "\n".join(align_cont) + "\n"
if __name__ == "__main__":
print('''***************** tbgen - Auto generate a testbench. *****************
Author: Xiongfei(Alex) Guo <xfguo@credosemi.com>
License: Beerware
''')
ofile_name = None
aparse = argparse.ArgumentParser(description='Automatically generate Verilog testbench')
aparse.add_argument('input_file', help='input Verilog file')
aparse.add_argument('output_file', help='output Verilog testbench', nargs='?', default=None)
aparse.add_argument('-p','--period', type=int, help='set period in clock ticks (default=10)', default=10)
aparse.add_argument('-t','--timescale',help='set timescale (default=1ns/10ps)', default='1ns/10ps')
aparse.add_argument('-d','--dumpfile',help='set dumpfile (default=tb_output.vcd)', default='tb_output.vcd')
aparse.add_argument('-l','--level', type=int, help='set dump depth level (usually 0,1, or 2; default=2)', default=2)
aparse.add_argument('-r','--resetneg', help='set reset to negative (default positive)', action='store_const', const=1, default=0)
aparse.add_argument('-i','--include', help='sets user include file name (default=user.tb_<name>.v)', default=None)
args = aparse.parse_args()
tbg = TestbenchGenerator(args.input_file, args.output_file)
tbg.print_module_head(args.timescale)
tbg.print_wires()
tbg.print_dut()
tbg.print_clock_gen(args.period,args.dumpfile,args.level,args.resetneg)
tbg.print_module_end(args.include)
tbg.close()

63
blinky/upduino.pcf Normal file
View File

@@ -0,0 +1,63 @@
# The LED pins are on dedicated pins and cannot be modified!
# Note that they are negative logic (write a 0 to turn on).
# These are also brought out to the left side of the board.
# Cut the board trace on jumper R28 to disable the onboard 3 color LED.
set_io -nowarn led_green 39
set_io -nowarn led_red 41
set_io -nowarn led_blue 40
# FTDI chip interface
set_io -nowarn serial_txd 14 # FPGA transmit to USB
set_io -nowarn serial_rxd 15 # FPGA receive from USB
set_io -nowarn spi_cs 16 # Connected to SPI flash, drive high unless using SPI flash!
# If using the FTDI MPSSE engine, the following signals apply
set_io -nowarn spi_sck 15 # Shared with the flash
set_io -nowarn spi_ssn 16 # Connected to SPI flash, drive high unless using SPI flash!
set_io -nowarn spi_mosi 17 # Shared with the flash
set_io -nowarn spi_miso 14 # Shared with the flash
# Normal GPIO pins, left side
set_io -nowarn gpio_23 23
set_io -nowarn gpio_25 25
set_io -nowarn gpio_26 26
set_io -nowarn gpio_27 27
set_io -nowarn gpio_32 32
set_io -nowarn gpio_35 35
set_io -nowarn gpio_31 31
set_io -nowarn gpio_37 37
set_io -nowarn gpio_34 34
set_io -nowarn gpio_43 43
set_io -nowarn gpio_36 36
set_io -nowarn gpio_42 42
set_io -nowarn gpio_38 38
set_io -nowarn gpio_28 28
# Normal GPIO pins, right side
# Following pins are added on the v3.0 of the board.
# SPI pins are brought out to the right side of the board
# Note: On board 12MHz clock can be brought to IOB_25B_G3 (pin 20) which is a global
# clock input. Short across R16 (labelled OSC on the board) to enable 12MHz clock to
# pin 20.
set_io -nowarn gpio_20 20
set_io -nowarn gpio_10 10
# Following are also found on v2.x of the UPduino
set_io -nowarn gpio_12 12
set_io -nowarn gpio_21 21
set_io -nowarn gpio_13 13
set_io -nowarn gpio_19 19
set_io -nowarn gpio_18 18
set_io -nowarn gpio_11 11
set_io -nowarn gpio_9 9
set_io -nowarn gpio_6 6
set_io -nowarn gpio_44 44
set_io -nowarn gpio_4 4
set_io -nowarn gpio_3 3
set_io -nowarn gpio_48 48
set_io -nowarn gpio_45 45
set_io -nowarn gpio_47 47
set_io -nowarn gpio_46 46
set_io -nowarn gpio_2 2

4
upduino/apio.ini Normal file
View File

@@ -0,0 +1,4 @@
[env]
board = upduino31
top-module = main

53
upduino/leds.v Normal file
View File

@@ -0,0 +1,53 @@
// More info about the primitve led control block SB_RGBA_DRV:
// https://github.com/tinyvision-ai-inc/UPduino-v3.0/blob/master/RTL/blink_led/rgb_blink.v
// https://blog.idorobots.org/entries/upduino-fpga-tutorial.html
module leds (
input clk,
input red_en,
input green_en,
input blue_en,
output wire [2:0] leds_out // [0]=red, [1]=green, [2]=blue
);
// Intensity multiplier for all three channels.
localparam integer IntensityAll = 2;
// Intensity controls in the range [0, 512].
localparam integer IntensityRed = 4 * IntensityAll;
localparam integer IntensityGreen = 2 * IntensityAll;
localparam integer IntensityBlue = 24 * IntensityAll;
// Frequency divider generate the PWM counter.
reg [15:0] divider;
// 9 bits PWM counter.
wire [ 8:0] pwm_counter = divider[15:7];
wire pwm_red = pwm_counter < IntensityRed;
wire pwm_green = pwm_counter < IntensityGreen;
wire pwm_blue = pwm_counter < IntensityBlue;
// Behavior.
always @(posedge clk) begin
divider <= divider + 1'b1;
end
// Instantiate the RGB current source primitive.
SB_RGBA_DRV #(
.CURRENT_MODE("0b1"), // "0b0" -> full current, "0b1" -> half current.
.RGB0_CURRENT("0b000001"),
.RGB1_CURRENT("0b000001"),
.RGB2_CURRENT("0b000001")
) RGB_DRIVER (
.RGBLEDEN(1'b1),
.RGB0PWM (green_en && pwm_green), // Green led input.
.RGB1PWM (blue_en & pwm_blue), // Blue led input.
.RGB2PWM (red_en & pwm_red), // Red led input.
.CURREN (1'b1),
.RGB0 (leds_out[1]), // Current regulated output to green led.
.RGB1 (leds_out[2]), // Current regulated output to blue led.
.RGB2 (leds_out[0]) // Current regulated output to red led.
);
endmodule

8
upduino/main.pcf Normal file
View File

@@ -0,0 +1,8 @@
# Mapping of the design signals to FPGA pins.
# The full Upduino3 PCF file is availble at:
# https://github.com/tinyvision-ai-inc/UPduino-v3.0/tree/master/RTL/common
set_io leds_out[0] 41 # Red.
set_io leds_out[1] 39 # Green.
set_io leds_out[2] 40 # Blue.

30
upduino/main.v Normal file
View File

@@ -0,0 +1,30 @@
// Generates a RGB blinking pattern. Uses the internal oscilator.
module main (
// [0]=red, [1]=green, [2]=blue. See pin definitions in main.pcf
output [2:0] leds_out
);
wire clk;
oscilator oscilator (.clk(clk));
// Frequency divider to generate the RGB patterns.
reg [25:0] divider;
wire red_en = divider[25] & divider[24];
wire green_en = divider[25] & ~divider[24];
wire blue_en = ~divider[25] & divider[24];
leds leds (
.clk(clk),
.red_en(red_en),
.green_en(green_en),
.blue_en(blue_en),
.leds_out(leds_out)
);
// Behavior.
always @(posedge clk) begin
divider <= divider + 1'b1;
end
endmodule

14
upduino/oscilator.v Normal file
View File

@@ -0,0 +1,14 @@
// Instanciates the internal high speed oscilator. This is needed
// only if the external Upduino oscilator is not connected to the FPGA.
module oscilator (
output clk
);
SB_HFOSC interal_osc (
.CLKHFPU(1'b1),
.CLKHFEN(1'b1),
.CLKHF (clk)
);
endmodule