Skip to content

Commit 9ffbf34

Browse files
committed
Change implementation of SPI, add test bench, update docs
1 parent 344361e commit 9ffbf34

File tree

7 files changed

+109
-86
lines changed

7 files changed

+109
-86
lines changed

README.md

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,9 @@
11
![](../../workflows/gds/badge.svg) ![](../../workflows/docs/badge.svg) ![](../../workflows/test/badge.svg) ![](../../workflows/fpga/badge.svg)
22

3-
# Tiny Tapeout Verilog Project Template
3+
# Simple SPI Test
44

5-
- [Read the documentation for project](docs/info.md)
5+
(c) 2024 Harald Pretl, Institute for Integrated Circuits, Johannes Kepler University, Linz, Austria
66

7-
## What is Tiny Tapeout?
7+
This project implements a simple SPI where the loaded 16b data can be output in 8b chunks (high and low byte). In addition, a magic cookie detection is implemented (an output goes active on detection of 0xCAFE).
88

9-
Tiny Tapeout is an educational project that aims to make it easier and cheaper than ever to get your digital and analog designs manufactured on a real chip.
10-
11-
To learn more and get started, visit https://tinytapeout.com.
12-
13-
## Set up your Verilog project
14-
15-
1. Add your Verilog files to the `src` folder.
16-
2. Edit the [info.yaml](info.yaml) and update information about your project, paying special attention to the `source_files` and `top_module` properties. If you are upgrading an existing Tiny Tapeout project, check out our [online info.yaml migration tool](https://tinytapeout.github.io/tt-yaml-upgrade-tool/).
17-
3. Edit [docs/info.md](docs/info.md) and add a description of your project.
18-
4. Adapt the testbench to your design. See [test/README.md](test/README.md) for more information.
19-
20-
The GitHub action will automatically build the ASIC files using [OpenLane](https://www.zerotoasiccourse.com/terminology/openlane/).
21-
22-
## Enable GitHub actions to build the results page
23-
24-
- [Enabling GitHub Pages](https://tinytapeout.com/faq/#my-github-action-is-failing-on-the-pages-part)
25-
26-
## Resources
27-
28-
- [FAQ](https://tinytapeout.com/faq/)
29-
- [Digital design lessons](https://tinytapeout.com/digital_design/)
30-
- [Learn how semiconductors work](https://tinytapeout.com/siliwiz/)
31-
- [Join the community](https://tinytapeout.com/discord)
32-
- [Build your design locally](https://www.tinytapeout.com/guides/local-hardening/)
33-
34-
## What next?
35-
36-
- [Submit your design to the next shuttle](https://app.tinytapeout.com/).
37-
- Edit [this README](README.md) and explain your design, how it works, and how to test it.
38-
- Share your project on your social network of choice:
39-
- LinkedIn [#tinytapeout](https://www.linkedin.com/search/results/content/?keywords=%23tinytapeout) [@TinyTapeout](https://www.linkedin.com/company/100708654/)
40-
- Mastodon [#tinytapeout](https://chaos.social/tags/tinytapeout) [@matthewvenn](https://chaos.social/@matthewvenn)
41-
- X (formerly Twitter) [#tinytapeout](https://twitter.com/hashtag/tinytapeout) [@tinytapeout](https://twitter.com/tinytapeout)
9+
The input and output list documentation can be found in `info.yaml`.

info.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ project:
1818
# Don't forget to also update `PROJECT_SOURCES` in test/Makefile.
1919
source_files:
2020
- "tt_um_hpretl_spi.v"
21-
- "chain1.v"
21+
- "chain2.v"
2222

2323
# The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins.
2424
pinout:
2525
# Inputs
26-
ui[0]: "clk (SCLK"
27-
ui[1]: "data_in (MOSI)"
28-
ui[2]: "select output byte (0 = low, 1 = high)"
29-
ui[3]: ""
26+
ui[0]: "SPI clk (SCLK)"
27+
ui[1]: "SPI data in (MOSI)"
28+
ui[2]: "SPI load (CS)"
29+
ui[3]: "select output byte (0 = low, 1 = high)"
3030
ui[4]: ""
3131
ui[5]: ""
3232
ui[6]: ""
3333
ui[7]: ""
3434

3535
# Outputs
36-
uo[0]: "data_out (MISO)"
36+
uo[0]: "SPI data out (MISO)"
3737
uo[1]: "cookie detected (loaded 0xCAFE)"
38-
uo[2]: "XOR of clk and data_in"
38+
uo[2]: "XOR of SPI clk and SPI data in"
3939
uo[3]: ""
4040
uo[4]: ""
4141
uo[5]: ""

src/chain1.v renamed to src/chain2.v

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,60 @@
1111
1212
IO description:
1313
14-
i_clk (like SPI SCLK)
15-
i_dat (like SPI MOSI)
16-
i_load (like SPI nCS)
17-
o_dat (like SPI MISO)
14+
i_clk (high running clock to sample the SPI input lines)
15+
i_spi_clk (like SPI SCLK)
16+
i_spi_dat (like SPI MOSI)
17+
i_spi_load (like SPI nCS)
18+
o_spi_dat (like SPI MISO)
1819
o_det (magic cookie detected)
1920
o_check (XOR of i_dat and i_load)
2021
*/
2122

22-
`ifndef __CHAIN1__
23-
`define __CHAIN1__
23+
`ifndef __CHAIN2__
24+
`define __CHAIN2__
2425
`default_nettype none
2526

26-
module chain1 (
27+
module chain2 (
28+
input wire i_resetn,
2729
input wire i_clk,
28-
input wire i_dat,
29-
input wire i_load,
30-
output wire o_dat,
30+
input wire i_spi_clk,
31+
input wire i_spi_dat,
32+
input wire i_spi_load,
33+
output wire o_spi_dat,
3134
output wire o_det,
3235
output wire o_check,
3336
output wire [15:0] o_data
3437
);
3538

3639
reg [15:0] scan_r, data_r;
40+
reg last_spi_clk_r;
3741

3842
// here we see if the stored content is matching a magic cookie
3943
assign o_det = (data_r === 16'hcafe) ? 1'b1 : 1'b0;
4044
// provide loaded data
4145
assign o_data = data_r;
4246
// shift out MSB register bit
43-
assign o_dat = scan_r[15];
47+
assign o_spi_dat = scan_r[15];
4448

4549
// here the scan chain, shift MSB first (as in SPI)
46-
always @(posedge i_clk) begin
47-
if (i_load === 1'b0)
48-
scan_r <= {scan_r[14:0],i_dat};
49-
end
50+
always @(posedge i_clk or negedge i_resetn) begin
51+
if (i_resetn === 1'b0) begin
52+
scan_r <= 16'b0;
53+
data_r <= 16'b0;
54+
last_spi_clk_r <= 1'b0;
55+
end else begin
56+
if ((i_spi_load === 1'b0) && (i_spi_clk === 1'b1) && (last_spi_clk_r === 1'b0))
57+
scan_r <= {scan_r[14:0],i_spi_dat};
58+
59+
if (i_spi_load === 1'b1)
60+
data_r <= scan_r;
5061

51-
// and here we latch the result
52-
always @(posedge i_load) begin
53-
data_r <= scan_r;
62+
last_spi_clk_r <= i_spi_clk;
63+
end
5464
end
5565

5666
// here a very simple boolean function to check basic functionality
57-
assign o_check = i_dat ^ i_load;
67+
assign o_check = i_spi_dat ^ i_spi_load;
5868

59-
endmodule // chain1
69+
endmodule // chain2
6070
`endif

src/tt_um_hpretl_spi.v

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
`default_nettype none
7-
`include "chain1.v"
7+
`include "chain2.v"
88

99
module tt_um_hpretl_spi (
1010
input wire [7:0] ui_in, // Dedicated inputs
@@ -20,13 +20,15 @@ module tt_um_hpretl_spi (
2020
wire [15:0] out_w;
2121

2222
assign uio_oe = 8'b11111111; // using IO for output
23-
assign uio_out = ui_in[2] ? out_w[15:8] : out_w[7:0];
23+
assign uio_out = ui_in[3] ? out_w[15:8] : out_w[7:0];
2424

25-
chain1 dut(
25+
chain2 dut(
26+
.i_resetn(rst_n),
2627
.i_clk(clk),
27-
.i_dat(ui_in[0]),
28-
.i_load(ui_in[1]),
29-
.o_dat(uo_out[0]),
28+
.i_spi_clk(ui_in[0]),
29+
.i_spi_dat(ui_in[1]),
30+
.i_spi_load(ui_in[2]),
31+
.o_spi_dat(uo_out[0]),
3032
.o_det(uo_out[1]),
3133
.o_check(uo_out[2]),
3234
.o_data(out_w)
@@ -36,6 +38,6 @@ module tt_um_hpretl_spi (
3638
assign uo_out[7:3] = 5'b10000;
3739

3840
// List all unused inputs to prevent warnings
39-
wire _unused = &{ena, rst_n, uio_in[7:0], ui_in[7:3], 1'b0};
41+
wire _unused = &{ena, uio_in[7:0], ui_in[7:4], 1'b0};
4042

4143
endmodule // tt_um_hpretl_spi

test/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
SIM ?= icarus
66
TOPLEVEL_LANG ?= verilog
77
SRC_DIR = $(PWD)/../src
8-
PROJECT_SOURCES = project.v
8+
PROJECT_SOURCES = tt_um_hpretl_spi.v
99

1010
ifneq ($(GATES),yes)
1111

test/tb.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module tb ();
2424
wire [7:0] uio_oe;
2525

2626
// Replace tt_um_example with your module name:
27-
tt_um_example user_project (
27+
tt_um_hpretl_spi user_project (
2828
.ui_in (ui_in), // Dedicated inputs
2929
.uo_out (uo_out), // Dedicated outputs
3030
.uio_in (uio_in), // IOs: Input path

test/test.py

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
# SPDX-FileCopyrightText: © 2024 Tiny Tapeout
1+
# SPDX-FileCopyrightText: © 2024 Tiny Tapeout and Harald Pretl, IIC@JKU
22
# SPDX-License-Identifier: Apache-2.0
33

44
import cocotb
55
from cocotb.clock import Clock
66
from cocotb.triggers import ClockCycles
77

8-
98
@cocotb.test()
109
async def test_project(dut):
1110
dut._log.info("Start")
1211

13-
# Set the clock period to 10 us (100 KHz)
14-
clock = Clock(dut.clk, 10, units="us")
12+
# Set the clock period to 0.1 us (10 MHz)
13+
clock = Clock(dut.clk, 0.1, units="us")
1514
cocotb.start_soon(clock.start())
1615

1716
# Reset
@@ -20,21 +19,65 @@ async def test_project(dut):
2019
dut.ui_in.value = 0
2120
dut.uio_in.value = 0
2221
dut.rst_n.value = 0
23-
await ClockCycles(dut.clk, 10)
22+
await ClockCycles(dut.clk, 3)
2423
dut.rst_n.value = 1
2524

2625
dut._log.info("Test project behavior")
2726

28-
# Set the input values you want to test
29-
dut.ui_in.value = 20
30-
dut.uio_in.value = 30
27+
# Check that cookie not detected
28+
assert dut.uo_out.value[2] == 0
29+
30+
# Load magic cookie
31+
load_word = "0xCAFE"
32+
load_word_bin = format(int(load_word, 16), '016b')
33+
load_word_bits = [int(bit) for bit in load_word_bin]
34+
35+
dut._log.info("Loading " + str(load_word_bin))
36+
37+
# Shift cookie in
38+
for i in range(16):
39+
#b0 is clk, b1 is dat, b2 is load, b3 is select
40+
dut.ui_in.value = 1*0 + 2*load_word_bits[i] + 4*0 + 8*0
41+
42+
await ClockCycles(dut.clk, 3)
43+
44+
#b0 is clk, b1 is dat, b2 is load, b3 is select
45+
dut.ui_in.value = 1*1 + 2*load_word_bits[i] + 4*0 + 8*0
46+
47+
await ClockCycles(dut.clk, 3)
48+
49+
assert (dut.uo_out.value & 2) == 0
3150

32-
# Wait for one clock cycle to see the output values
51+
# serial register is loaded, now store it
52+
#b0 is clk, b1 is dat, b2 is load, b3 is select
53+
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0
54+
await ClockCycles(dut.clk, 3)
55+
dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0
56+
await ClockCycles(dut.clk, 3)
57+
58+
# check magic cookie detection
59+
dut._log.info("Check magic cookie detection")
60+
assert (dut.uo_out.value & 2) == 2
61+
62+
# check output parallel selection
63+
dut._log.info("Check parallel output")
64+
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0
65+
await ClockCycles(dut.clk, 1)
66+
assert dut.uio_out.value == 0xfe
67+
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*1
3368
await ClockCycles(dut.clk, 1)
69+
assert dut.uio_out.value == 0xca
70+
71+
# Shift 0 in
72+
for i in range(16):
73+
#b0 is clk, b1 is dat, b2 is load, b3 is select
74+
dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0
75+
76+
await ClockCycles(dut.clk, 3)
77+
78+
#b0 is clk, b1 is dat, b2 is load, b3 is select
79+
dut.ui_in.value = 1*1 + 2*0 + 4*0 + 8*0
3480

35-
# The following assersion is just an example of how to check the output values.
36-
# Change it to match the actual expected output of your module:
37-
assert dut.uo_out.value == 50
81+
await ClockCycles(dut.clk, 3)
3882

39-
# Keep testing the module by changing the input values, waiting for
40-
# one or more clock cycles, and asserting the expected output values.
83+
assert (dut.uo_out.value & 2) == 2

0 commit comments

Comments
 (0)