diff --git a/.gitignore b/.gitignore index 957c3ef..9d496ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -simtop_* +tests/test_* diff --git a/Makefile b/Makefile deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index 6570060..c31801a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ License, v.2.0. A copy of this license may be found in the file `COPYING`. You can obtain one at https://mozilla.org/MPL/2.0/. All Verilog source in this project is dual-licensed under the MPL v2.0 -and the CERN-OHL-W v2.0 (or any later version). +and the CERN-OHL-W v2.0. ## Tests @@ -18,11 +18,6 @@ Run `./mk.sh` in `tests/` to generate and run tests. ## Modules -"master_no_read" and "slave_no_write" have no Master In, Slave Out ("miso") -wires (and no corresponding shift registers), while "master_no_write" -and "slave_no_read" have no Master Out, Slave In ("mosi") wires. This -is for compatability for "SPI compatible" devices that are read only. - "master_ss" and others include a timer that will assert the Slave Select pin and wait a set number of clock cycles before starting the SPI transfer. diff --git a/spi_master.v b/spi_master.v index 715ad98..5f478d7 100644 --- a/spi_master.v +++ b/spi_master.v @@ -1,7 +1,7 @@ -/* (c) Peter McGoron 2022 v0.4 - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v.2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. +/* (c) Peter McGoron 2022-2024 v0.4 + * + * This code is disjunctively dual-licensed under the MPL v2.0, or the + * CERN-OHL-W v2. */ /* CYCLE_HALF_WAIT should take into account the setup time of the slave @@ -45,7 +45,7 @@ module spi_master #( reg miso_hot = 0; reg read_miso = 0; -always @ (posedge clk) if (ENABLE_MISO) begin +always @ (posedge clk) if (ENABLE_MISO == 1) begin read_miso <= miso_hot; miso_hot <= miso; end @@ -70,20 +70,20 @@ task idle_state(); end else begin sck <= 1; end - if (ENABLE_MOSI) mosi <= 0; + if (ENABLE_MOSI == 1) mosi <= 0; timer <= 0; bit_counter <= 0; endtask task read_data(); - if (ENABLE_MISO) begin + if (ENABLE_MISO == 1) begin from_slave <= from_slave << 1; from_slave[0] <= read_miso; end endtask task write_data(); - if (ENABLE_MOSI) begin + if (ENABLE_MOSI == 1) begin mosi <= send_buf[WID-1]; send_buf <= send_buf << 1; end @@ -97,13 +97,13 @@ task setup_bits(); * For mode 01 and mode 10, the first action is a WRITE. */ if (POLARITY == PHASE) begin - if (ENABLE_MOSI) begin + if (ENABLE_MOSI == 1) begin mosi <= to_slave[WID-1]; send_buf <= to_slave << 1; end state <= CYCLE_WAIT; end else begin - if (ENABLE_MISO) begin + if (ENABLE_MISO == 1) begin send_buf <= to_slave; end state <= ON_CYCLE; @@ -128,8 +128,8 @@ always @ (posedge clk) begin finished <= 0; state <= WAIT_ON_ARM; ready_to_arm <= 1; - if (ENABLE_MISO) from_slave <= 0; - if (ENABLE_MOSI) send_buf <= 0; + if (ENABLE_MISO == 1) from_slave <= 0; + if (ENABLE_MOSI == 1) send_buf <= 0; end else case (state) WAIT_ON_ARM: begin `ifdef SIMULATION diff --git a/spi_master_ss_template.v b/spi_master_ss.v similarity index 91% rename from spi_master_ss_template.v rename to spi_master_ss.v index e5624ce..f71a874 100644 --- a/spi_master_ss_template.v +++ b/spi_master_ss.v @@ -1,7 +1,7 @@ /* (c) Peter McGoron 2022 v0.4 - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v.2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * This code is disjunctively dual-licensed under the MPL v2.0, or the + * CERN-OHL-W v2. */ /* spi master with integrated ability to wait a certain amount of cycles diff --git a/spi_slave.v b/spi_slave.v index 3530b5e..be1371f 100644 --- a/spi_slave.v +++ b/spi_slave.v @@ -1,7 +1,7 @@ /* (c) Peter McGoron 2022 v0.4 - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v.2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * This code is disjunctively dual-licensed under the MPL v2.0, or the + * CERN-OHL-W v2. */ module spi_slave #( @@ -32,7 +32,7 @@ module spi_slave #( reg mosi_hot = 0; reg read_mosi = 0; -always @ (posedge clk) if (ENABLE_MOSI) begin +always @ (posedge clk) if (ENABLE_MOSI == 1) begin read_mosi <= mosi_hot; mosi_hot <= mosi; end @@ -48,21 +48,21 @@ reg [WID-1:0] send_buf = 0; `endif task read_data(); - if (ENABLE_MOSI) begin + if (ENABLE_MOSI == 1) begin from_master <= from_master << 1; from_master[0] <= read_mosi; end endtask task write_data(); - if (ENABLE_MISO) begin + if (ENABLE_MISO == 1) begin send_buf <= send_buf << 1; miso <= send_buf[WID-1]; end endtask task setup_bits(); - if (ENABLE_MISO) begin + if (ENABLE_MISO == 1) begin /* at Mode 00, the transmission starts with * a rising edge, and at mode 11, it starts with a falling * edge. For both modes, these are READs. @@ -93,9 +93,9 @@ always @ (posedge clk) begin ss_delay <= 0; ready_at_start <= 0; - if (ENABLE_MOSI) from_master <= 0; + if (ENABLE_MOSI == 1) from_master <= 0; - if (ENABLE_MISO) begin + if (ENABLE_MISO == 1) begin miso <= 0; send_buf <= 0; end diff --git a/tests/clean.sh b/tests/clean.sh index 9d50089..238fbaf 100755 --- a/tests/clean.sh +++ b/tests/clean.sh @@ -1,3 +1,3 @@ #!/bin/sh -rm -rf simtop_* +rm -rf test_master* test_ss* diff --git a/tests/mk.sh b/tests/mk.sh deleted file mode 100755 index 707b753..0000000 --- a/tests/mk.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/sh - -run_test() { - POL=$1 - PHASE=$2 - MASTER_TYPE=$3 - SLAVE_TYPE=$4 - DIR=$5 - WID=$6 - MODS=$7 - EXTARG=$8 - WIDLEN=$(printf "import math\nprint(math.floor(math.log2($WID) + 1))" | python3 -) - - echo "running $POL$PHASE $MASTER_TYPE" - - verilator --cc --exe -I.. -Wall -Wno-unused --trace --trace-fst \ - --top-module simtop \ - -GPOLARITY=$POL -GPHASE=$PHASE -GWID=$WID -CFLAGS -DWID=$WID \ - -GWID_LEN=$WIDLEN \ - -DSPI_MASTER_TYPE=$MASTER_TYPE -DSPI_SLAVE_TYPE=$SLAVE_TYPE \ - -DVCDFILE="\"$DIR.fst\"" \ - -DSIMULATION \ - --Mdir $DIR \ - $EXTARG \ - simtop.v write_read.cpp $MODS \ - || exit 1 - - cd "$DIR" - make -f Vsimtop.mk - ./Vsimtop -} - -for POL in 0 1; do - for PHASE in 0 1; do - ( \ - run_test $POL $PHASE \ - spi_master spi_slave \ - simtop_$POL$PHASE 24 \ - "../spi_master.v ../spi_slave.v" - ) || exit 1 - - ( \ - run_test $POL $PHASE \ - spi_master_ss spi_slave \ - simtop_ss$POL$PHASE 24 \ - "../spi_master_ss.v ../spi_slave.v" \ - "-DSPI_MASTER_SS -CFLAGS -DSPI_MASTER_SS" - ) || exit 1 - - ( \ - run_test $POL $PHASE \ - spi_master_no_write spi_slave_no_read \ - simtop_no_write_$POL$PHASE 24 \ - "../spi_master_no_write.v ../spi_slave_no_read.v" \ - "-DSPI_MASTER_NO_WRITE -CFLAGS -DSPI_MASTER_NO_WRITE" - ) || exit 1 - - ( \ - run_test $POL $PHASE \ - spi_master_ss_no_write spi_slave_no_read \ - simtop_ss_no_write_$POL$PHASE 24 \ - "../spi_master_ss_no_write.v ../spi_slave_no_read.v" \ - "-DSPI_MASTER_NO_WRITE -CFLAGS -DSPI_MASTER_NO_WRITE - -DSPI_MASTER_SS -CFLAGS -DSPI_MASTER_SS" - ) || exit 1 - - ( \ - run_test $POL $PHASE \ - spi_master_no_read spi_slave_no_write \ - simtop_no_read_$POL$PHASE 24 \ - "../spi_master_no_read.v ../spi_slave_no_write.v" \ - "-DSPI_MASTER_NO_READ -CFLAGS -DSPI_MASTER_NO_READ" - ) || exit 1 - - ( \ - run_test $POL $PHASE \ - spi_master_ss_no_read spi_slave_no_write \ - simtop_ss_no_read_$POL$PHASE 24 \ - "../spi_master_ss_no_read.v ../spi_slave_no_write.v" \ - "-DSPI_MASTER_NO_READ -CFLAGS -DSPI_MASTER_NO_READ - -DSPI_MASTER_SS -CFLAGS -DSPI_MASTER_SS" - ) || exit 1 - - done -done diff --git a/tests/runtests.py b/tests/runtests.py new file mode 100755 index 0000000..5b883bb --- /dev/null +++ b/tests/runtests.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 + +import math +import subprocess +import sys +import os +from collections import namedtuple + +class Case: + def __init__(self, master_name, defflags, params): + self.master_name = master_name + self.defflags = defflags + self.params = params + + def run(self, dirname, pol, phase, wid): + dirname = f'{dirname}{pol}{phase}' + widlen = math.floor(math.log2(wid) + 1) + args = ["verilator", "--cc", "--exe", "-I..", "-Wall", "--trace", + "--trace-fst", "-Wno-unused", + "--top-module", "simtop", + f"-GPOLARITY={pol}", f"-GPHASE={phase}", + f"-GWID={wid}", f"-CFLAGS", f"-DWID={wid}", + f"-GWID_LEN={widlen}", + f"-DSPI_MASTER_TYPE={self.master_name}", + f'-DVCDFILE="{dirname}/trace.fst"', + f"-DSIMULATION", + f"--Mdir", dirname + ] + + for flag in self.defflags: + args.extend([f'-D{flag}', '-CFLAGS', f'-D{flag}']) + for param in self.params: + args.append(f'-G{param}={self.params[param]}') + + args.extend(['simtop.v', 'write_read.cpp', '../spi_slave.v', f'../{self.master_name}.v']) + + print(args) + proc = subprocess.run(args, stdout=sys.stdout, stderr=sys.stderr) + if proc.returncode != 0: + print("Verilator failed") + return False + + os.chdir(dirname) + proc = subprocess.run(["make", "-f", "Vsimtop.mk"], stdout=sys.stdout, stderr=sys.stderr) + if proc.returncode != 0: + print("Make failed") + os.chdir("..") + return False + + proc = subprocess.run("./Vsimtop", stdout=sys.stdout, stderr=sys.stderr) + os.chdir("..") + if proc.returncode != 0: + print("Vsimtop failed") + return False + return True + +cases = {} +# Add basic cases +cases[f'test_master'] = Case("spi_master", [], {}) +cases[f'test_ss'] = Case("spi_master_ss", ["SPI_MASTER_SS"], {}) +cases[f'test_master_no_write'] = Case("spi_master", ['SPI_MASTER_NO_WRITE'], {'ENABLE_MOSI': 0}) +cases[f'test_ss_no_write'] = Case("spi_master_ss", ["SPI_MASTER_SS", 'SPI_MASTER_NO_WRITE'], {'ENABLE_MOSI': 0}) +cases[f'test_master_no_read'] = Case("spi_master", ['SPI_MASTER_NO_READ'], {'ENABLE_MISO': 1}) +cases[f'test_ss_no_read'] = Case("spi_master_ss", ["SPI_MASTER_SS", 'SPI_MASTER_NO_READ'], {'ENABLE_MISO': 1}) + +failures=0 +for polarity in [0, 1]: + for phase in [0, 1]: + for casename in cases: + if not cases[casename].run(casename, polarity, phase, 24): + failures += 1 +print(f'Failures: {failures}') diff --git a/tests/simtop.v b/tests/simtop.v index 1e6662a..c280633 100644 --- a/tests/simtop.v +++ b/tests/simtop.v @@ -1,11 +1,13 @@ -/* (c) Peter McGoron 2022 - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v.2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. +/* (c) Peter McGoron 2022-2024 v0.4 + * + * This code is disjunctively dual-licensed under the MPL v2.0, or the + * CERN-OHL-W v2. */ module simtop #( + parameter ENABLE_MOSI = 1, + parameter ENABLE_MISO = 1, parameter POLARITY = 0, parameter PHASE = 0, parameter WID = 24, @@ -13,14 +15,13 @@ module simtop ) ( input clk, input rst_L, -`ifndef SPI_MASTER_NO_WRITE + input [WID-1:0] master_to_slave, output [WID-1:0] from_master, -`endif -`ifndef SPI_MASTER_NO_READ + input [WID-1:0] slave_to_master, output [WID-1:0] from_slave, -`endif + input activate, `ifndef SPI_MASTER_SS input ss, @@ -31,13 +32,9 @@ module simtop output err ); -`ifndef SPI_MASTER_NO_READ wire miso; -`endif -`ifndef SPI_MASTER_NO_WRITE wire mosi; -`endif wire sck; wire ss_L; @@ -46,8 +43,8 @@ wire ss_L; assign ss_L = !ss; `endif -reg slave_finished; -reg slave_error; +wire slave_finished; +wire slave_error; `SPI_MASTER_TYPE #( @@ -55,6 +52,8 @@ reg slave_error; .SS_WAIT(5), .SS_WAIT_TIMER_LEN(3), `endif + .ENABLE_MOSI(ENABLE_MOSI), + .ENABLE_MISO(ENABLE_MISO), .CYCLE_HALF_WAIT(5), .TIMER_LEN(3), .POLARITY(POLARITY), @@ -64,14 +63,10 @@ reg slave_error; ) master ( .clk(clk), .rst_L(rst_L), -`ifndef SPI_MASTER_NO_WRITE .to_slave(master_to_slave), .mosi(mosi), -`endif -`ifndef SPI_MASTER_NO_READ .from_slave(from_slave), .miso(miso), -`endif `ifdef SPI_MASTER_SS .ss_L(ss_L), `endif @@ -81,7 +76,9 @@ reg slave_error; .arm(activate) ); -`SPI_SLAVE_TYPE #( +spi_slave #( + .ENABLE_MOSI(ENABLE_MOSI), + .ENABLE_MISO(ENABLE_MISO), .POLARITY(POLARITY), .PHASE(PHASE), .WID(WID), @@ -91,14 +88,10 @@ reg slave_error; .rst_L(rst_L), .sck(sck), .ss_L(ss_L), -`ifndef SPI_MASTER_NO_WRITE .from_master(from_master), .mosi(mosi), -`endif -`ifndef SPI_MASTER_NO_READ .to_master(slave_to_master), .miso(miso), -`endif .finished(slave_finished), .rdy(rdy), .err(err)