Skip to content

Commit

Permalink
Test for chipignite
Browse files Browse the repository at this point in the history
  • Loading branch information
rejunity committed May 27, 2024
1 parent c672466 commit 79b4450
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 1 deletion.
43 changes: 43 additions & 0 deletions test_chipignite/Makefile
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
31 changes: 31 additions & 0 deletions test_chipignite/README.md
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
```
2 changes: 2 additions & 0 deletions test_chipignite/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest==8.1.1
cocotb==1.8.1
39 changes: 39 additions & 0 deletions test_chipignite/tb.gtkw
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
45 changes: 45 additions & 0 deletions test_chipignite/tb.v
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
199 changes: 199 additions & 0 deletions test_chipignite/test.py
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
3 changes: 2 additions & 1 deletion test_tinytapeout/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
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 tt_um_rejunity_z80.v
PROJECT_SOURCES = tv80/tv80_alu.v tv80/tv80_reg.v tv80/tv80_mcode.v tv80/tv80_core.v tv80/tv80s.v
PROJECT_SOURCES += tt_um_rejunity_z80.v

ifneq ($(GATES),yes)

Expand Down

0 comments on commit 79b4450

Please sign in to comment.