generated from TinyTapeout/tt07-verilog-template
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
361 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Makefile | ||
# See https://docs.cocotb.org/en/stable/quickstart.html for more info | ||
|
||
# defaults | ||
SIM ?= icarus | ||
TOPLEVEL_LANG ?= verilog | ||
SRC_DIR = $(PWD)/../src | ||
PROJECT_SOURCES = tv80/tv80_alu.v tv80/tv80_reg.v tv80/tv80_mcode.v tv80/tv80_core.v tv80/tv80s.v | ||
PROJECT_SOURCES += ci2406_z80.v | ||
|
||
ifneq ($(GATES),yes) | ||
|
||
# RTL simulation: | ||
SIM_BUILD = sim_build/rtl | ||
VERILOG_SOURCES += $(addprefix $(SRC_DIR)/,$(PROJECT_SOURCES)) | ||
COMPILE_ARGS += -I$(SRC_DIR) | ||
|
||
else | ||
|
||
# Gate level simulation: | ||
SIM_BUILD = sim_build/gl | ||
COMPILE_ARGS += -DGL_TEST | ||
COMPILE_ARGS += -DFUNCTIONAL | ||
COMPILE_ARGS += -DUSE_POWER_PINS | ||
COMPILE_ARGS += -DSIM | ||
COMPILE_ARGS += -DUNIT_DELAY=\#1 | ||
VERILOG_SOURCES += $(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/verilog/primitives.v | ||
VERILOG_SOURCES += $(PDK_ROOT)/sky130A/libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v | ||
|
||
# this gets copied in by the GDS action workflow | ||
VERILOG_SOURCES += $(PWD)/gate_level_netlist.v | ||
|
||
endif | ||
|
||
# Include the testbench sources: | ||
VERILOG_SOURCES += $(PWD)/tb.v | ||
TOPLEVEL = tb | ||
|
||
# MODULE is the basename of the Python test file | ||
MODULE = test | ||
|
||
# include cocotb's make rules to take care of the simulator setup | ||
include $(shell cocotb-config --makefiles)/Makefile.sim |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Sample testbench for a Tiny Tapeout project | ||
|
||
This is a sample testbench for a Tiny Tapeout project. It uses [cocotb](https://docs.cocotb.org/en/stable/) to drive the DUT and check the outputs. | ||
See below to get started or for more information, check the [website](https://tinytapeout.com/hdl/testing/). | ||
|
||
## Setting up | ||
|
||
1. Edit [Makefile](Makefile) and modify `PROJECT_SOURCES` to point to your Verilog files. | ||
2. Edit [tb.v](tb.v) and replace `tt_um_example` with your module name. | ||
|
||
## How to run | ||
|
||
To run the RTL simulation: | ||
|
||
```sh | ||
make -B | ||
``` | ||
|
||
To run gatelevel simulation, first harden your project and copy `../runs/wokwi/results/final/verilog/gl/{your_module_name}.v` to `gate_level_netlist.v`. | ||
|
||
Then run: | ||
|
||
```sh | ||
make -B GATES=yes | ||
``` | ||
|
||
## How to view the VCD file | ||
|
||
```sh | ||
gtkwave tb.vcd tb.gtkw | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pytest==8.1.1 | ||
cocotb==1.8.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
[*] | ||
[*] GTKWave Analyzer v3.4.0 (w)1999-2022 BSI | ||
[*] Mon Nov 20 16:00:28 2023 | ||
[*] | ||
[dumpfile] "/home/uri/p/tt-new-template-proto/test/tb.vcd" | ||
[dumpfile_mtime] "Mon Nov 20 15:58:34 2023" | ||
[dumpfile_size] 1110 | ||
[savefile] "/home/uri/p/tt-new-template-proto/test/tb.gtkw" | ||
[timestart] 0 | ||
[size] 1376 600 | ||
[pos] -1 -1 | ||
*-24.534533 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 | ||
[treeopen] tb. | ||
[sst_width] 297 | ||
[signals_width] 230 | ||
[sst_expanded] 1 | ||
[sst_vpaned_height] 158 | ||
@28 | ||
tb.user_project.ena | ||
@29 | ||
tb.user_project.clk | ||
@28 | ||
tb.user_project.rst_n | ||
@200 | ||
-Inputs | ||
@22 | ||
tb.user_project.ui_in[7:0] | ||
@200 | ||
-Bidirectional Pins | ||
@22 | ||
tb.user_project.uio_in[7:0] | ||
tb.user_project.uio_oe[7:0] | ||
tb.user_project.uio_out[7:0] | ||
@200 | ||
-Output Pins | ||
@22 | ||
tb.user_project.uo_out[7:0] | ||
[pattern_trace] 1 | ||
[pattern_trace] 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
`default_nettype none | ||
`timescale 1ns / 1ps | ||
|
||
/* This testbench just instantiates the module and makes some convenient wires | ||
that can be driven / tested by the cocotb test.py. | ||
*/ | ||
module tb (); | ||
// Dump the signals to a VCD file. You can view it with gtkwave. | ||
initial begin | ||
$dumpfile("tb.vcd"); | ||
$dumpvars(0, tb); | ||
#1; | ||
end | ||
|
||
// Wire up the inputs and outputs: | ||
reg clk; | ||
reg rst_n; | ||
wire [35:0] io_in; | ||
wire [35:0] io_out; | ||
wire [35:0] io_oeb; | ||
|
||
wire [3:0] controls_in; | ||
wire [7:0] controls_out = io_out[7:0]; | ||
wire [15:0] addr = io_out[23:8]; | ||
wire [7:0] data_in; | ||
wire [7:0] data_out = io_out[31:24]; | ||
wire [7:0] data_oe =~io_oeb[31:24]; | ||
assign io_in [35:32] = controls_in; | ||
assign io_in [31:24] = data_in; | ||
|
||
// Replace tt_um_example with your module name: | ||
ci2406_z80 user_project ( | ||
// Include power ports for the Gate Level test: | ||
`ifdef USE_POWER_PINS | ||
.vccd1 (1'b1), | ||
.vssd1 (1'b0), | ||
`endif | ||
.wb_clk_i(clk), // clock | ||
.rst_n (rst_n), // not reset | ||
.io_in (io_in), | ||
.io_out (io_out), | ||
.io_oeb (io_oeb) | ||
); | ||
|
||
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
# SPDX-FileCopyrightText: © 2024 ReJ aka Renaldsa Zioma | ||
# SPDX-License-Identifier: MIT | ||
|
||
import cocotb | ||
from cocotb.clock import Clock | ||
from cocotb.triggers import ClockCycles, FallingEdge, RisingEdge | ||
|
||
BUS_READY = 0b1111 # not WAIT, not INT, not NMI, not BUSRQ | ||
OPCODE_NOP = 0x00 | ||
OPCODE_LDHL = 0x21 | ||
OPCODE_LDNNA = 0x32 | ||
|
||
@cocotb.test() | ||
async def test__RESET(dut): | ||
await start(dut) | ||
|
||
dut._log.info("Test RESET sequence") | ||
dut._log.info("Reset") | ||
dut.io_in.value = 0 | ||
dut.rst_n.value = 0 | ||
await ClockCycles(dut.clk, 2) | ||
for z80_cycle in range(-4, 2): | ||
if (z80_cycle >= 0): | ||
dut.rst_n.value = 1 | ||
controls_1st_half, _, _, _, _, _ = await z80_step(dut, z80_cycle, verbose=True) | ||
controls = controls_1st_half | ||
assert controls['mreq'] == 0 | ||
assert controls['rd'] == 0 | ||
assert controls['wr'] == 0 | ||
assert controls['wr'] == 0 | ||
assert controls['ioreq'] == 0 | ||
assert controls['halt'] == 0 | ||
assert controls['busak'] == 0 | ||
assert controls['m1'] == (z80_cycle > 0) | ||
|
||
@cocotb.test() | ||
async def test__NOP(dut): | ||
await start_and_reset(dut) | ||
dut._log.info("Test NOP") | ||
|
||
dut.controls_in.value = BUS_READY | ||
dut.data_in.value = OPCODE_NOP | ||
cycles_per_instr = 4 | ||
|
||
z80_cycle = 0 | ||
for i in range(32): | ||
_, controls, addr, addr_, _, _ = await z80_step(dut, z80_cycle, verbose=True) | ||
|
||
assert addr == addr_ | ||
if (z80_cycle-1) % cycles_per_instr == 0 or \ | ||
(z80_cycle-1) % cycles_per_instr == 1: | ||
assert controls['m1'] == 1 | ||
if (z80_cycle-1) % cycles_per_instr == 1: | ||
assert controls['mreq'] == 1 | ||
assert controls['rd'] == 1 | ||
assert controls['wr'] == 0 | ||
assert controls['ioreq'] == 0 | ||
assert controls['halt'] == 0 | ||
assert controls['busak'] == 0 | ||
if z80_cycle > 1: | ||
assert addr == (z80_cycle - 1) // cycles_per_instr # Running NOPs, every 4 cycles address increases | ||
z80_cycle += 1 | ||
|
||
@cocotb.test() | ||
async def test__LD_HL2121(dut): | ||
await start_and_reset(dut) | ||
dut._log.info("Test LD HL, $2121") | ||
|
||
dut.controls_in.value = BUS_READY | ||
dut.data_in.value = OPCODE_LDHL # LD HL, $2121 | ||
cycles_per_instr = 10 | ||
|
||
z80_cycle = 0 | ||
for i in range(32): | ||
_, controls, addr, addr_, _, _ = await z80_step(dut, z80_cycle, verbose=True) | ||
|
||
assert addr == addr_ # Address is set during the 1st half-cycle and is stable until the end of the cycle | ||
if (z80_cycle-1) % cycles_per_instr == 0 or \ | ||
(z80_cycle-1) % cycles_per_instr == 1: | ||
assert controls['m1'] == 1 | ||
if (z80_cycle-1) % cycles_per_instr == 1 or \ | ||
(z80_cycle-1) % cycles_per_instr == 5 or \ | ||
(z80_cycle-1) % cycles_per_instr == 8: | ||
assert controls['mreq'] == 1 | ||
assert controls['rd'] == 1 | ||
assert controls['wr'] == 0 | ||
assert controls['ioreq'] == 0 | ||
assert controls['halt'] == 0 | ||
assert controls['busak'] == 0 | ||
z80_cycle += 1 | ||
|
||
@cocotb.test() | ||
async def test__LD_3232_A(dut): | ||
await start_and_reset(dut) | ||
dut._log.info("Test LD ($3232), A") | ||
|
||
# Set the input values you want to test | ||
dut.controls_in.value = BUS_READY | ||
dut.data_in.value = OPCODE_LDNNA # LD ($3232), A | ||
cycles_per_instr = 13 | ||
|
||
# Wait for one clock cycle to see the output values | ||
z80_cycle = 0 | ||
for i in range(32): | ||
_, controls, addr, addr_, data, data_ = await z80_step(dut, z80_cycle, verbose=True) | ||
|
||
assert addr == addr_ # Address is set during the 1st half-cycle and is stable until the end of the cycle | ||
if (z80_cycle-1) % cycles_per_instr == 0 or \ | ||
(z80_cycle-1) % cycles_per_instr == 1: | ||
assert controls['m1'] == 1 | ||
if (z80_cycle-1) % cycles_per_instr == 1 or \ | ||
(z80_cycle-1) % cycles_per_instr == 5 or \ | ||
(z80_cycle-1) % cycles_per_instr == 8: | ||
assert controls['mreq'] == 1 | ||
assert controls['rd'] == 1 | ||
assert controls['wr'] == 0 | ||
if (z80_cycle-1) % cycles_per_instr == 11: | ||
assert addr == 0x3232 | ||
assert data_ == 0xFF # A is set to 0xFF during RESET | ||
assert controls['mreq'] == 1 | ||
assert controls['wr'] == 1 | ||
assert controls['rd'] == 0 | ||
if (z80_cycle-1) % cycles_per_instr == 12 and z80_cycle > 0: | ||
assert data == 0xFF # databus is stable after /WR goes high | ||
if (z80_cycle-1) % cycles_per_instr > 11: | ||
assert controls['wr'] == 0 | ||
assert controls['rd'] == 0 | ||
assert controls['ioreq'] == 0 | ||
assert controls['halt'] == 0 | ||
assert controls['busak'] == 0 | ||
z80_cycle += 1 | ||
|
||
async def start(dut): | ||
dut._log.info("Start") | ||
|
||
# Set the clock period to ~62.5 ns (16 MHz = 4MHz Z80 clock) | ||
clock = Clock(dut.clk, 62, units="ns") | ||
cocotb.start_soon(clock.start()) | ||
|
||
async def start_and_reset(dut): | ||
await start(dut) | ||
|
||
# Reset | ||
dut._log.info("Reset") | ||
dut.io_in.value = 0 | ||
dut.rst_n.value = 0 | ||
await ClockCycles(dut.clk, 8) | ||
dut.rst_n.value = 1 | ||
|
||
async def z80_step(z80, cycle, verbose=False): | ||
def read_controls(): | ||
controls = [bit_n(z80.controls_out, n) for n in range(8)] | ||
return dict(zip(['halt', 'busak', 'm1', 'mreq', 'ioreq', 'rd', 'wr', 'rfsh'], controls)) | ||
# | 41 io[7] - 28| /RFSH --> | ||
# | 33 io[2] - 27| /M1 --> | ||
# | | | ||
# | | | ||
# | | | ||
# <-- /HALT |18 - io[0] 31 32 io[1] - 23| /BUSAK --> | ||
# <-- /MREQ |19 - io[3] 34 37 io[6] - 22| /WR --> | ||
# <-- /IORQ |20 - io[4] 35 36 io[5] - 21| /RD --> | ||
# `-------------------------------------' | ||
def read_data(): | ||
if z80.data_oe.value != 0b1111_1111: | ||
return 'ZZ' | ||
elif z80.data_out.value.is_resolvable: | ||
return int(z80.data_out.value.integer) | ||
else: | ||
return z80.data_out.value.binstr | ||
await FallingEdge(z80.clk) | ||
controls_f = read_controls() | ||
addr_f = z80.addr.value.integer | ||
data_f = read_data() | ||
await RisingEdge(z80.clk) | ||
controls_r = read_controls() | ||
addr_r = z80.addr.value.integer | ||
data_r = read_data() | ||
|
||
controls = controls_f | ||
addr = addr_r | ||
data = data_f | ||
if (verbose): | ||
print (f"clk: {cycle:3d} {controls} addr:0x{addr:04X}".replace("'", "") \ | ||
.replace("{", "") \ | ||
.replace("}", "") \ | ||
.replace(",", "")) | ||
if (controls['m1'] and controls['rd']): | ||
print(f" OPCODE: ${int(z80.data_in.value):02X}") | ||
elif (controls['rd']): | ||
print(f" READ DATA: ${int(z80.data_in.value):02X}") | ||
if (controls['wr'] == 1): | ||
print(f" WRITE DATA: ${data:02X}") | ||
return controls_f, controls_r, addr_f, addr_r, data_f, data_r | ||
|
||
def bit_n(signals, n): | ||
if signals[n].value.is_resolvable: | ||
return 1-signals[n].value.integer | ||
else: | ||
return signals[n].value.binstr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters