diff --git a/README.md b/README.md index 6fdee35..6570060 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,17 @@ and the CERN-OHL-W v2.0 (or any later version). ## Tests -Go into `tests` and run `make`. To rerun tests, run `make clean` followed -by `make`. +Run `./mk.sh` in `tests/` to generate and run tests. -The test for each mode generates a `.vcd` file which you may view using -[GTKWave][1]. You can use this to gauge which SPI mode is appropriate for -your device. +## Modules -[1]: https://gtkwave.sourceforge.net/ +"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. ## SPI Modes diff --git a/spi_master.v b/spi_master.v index 053686f..d8b9020 100644 --- a/spi_master.v +++ b/spi_master.v @@ -18,7 +18,9 @@ spi_master #( parameter WID = 24, // Width of bits per transaction. parameter WID_LEN = 5, // Length in bits required to store WID - parameter CYCLE_HALF_WAIT = 1, // Half of the wait time of a cycle + parameter CYCLE_HALF_WAIT = 1, // One less than half of the wait time of a cycle. + // One cycle of a transfer is 2(CYCLE_HALF_WAIT + 1) + // clock cycles. parameter TIMER_LEN = 3, // Length in bits required to store CYCLE_HALF_WAIT parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read. @@ -134,6 +136,7 @@ always @ (posedge clk) begin bit_counter <= bit_counter + 1; end end + state <= CYCLE_WAIT; end CYCLE_WAIT: begin diff --git a/spi_master_ss.v b/spi_master_ss.v new file mode 100644 index 0000000..6dc3429 --- /dev/null +++ b/spi_master_ss.v @@ -0,0 +1,4 @@ +`define SPI_MASTER_SS_NAME spi_master_ss +`define SPI_MASTER_NAME spi_master +/* verilator lint_off DECLFILENAME */ +`include "spi_master_ss_template.v" diff --git a/spi_master_ss_no_read.v b/spi_master_ss_no_read.v new file mode 100644 index 0000000..741e940 --- /dev/null +++ b/spi_master_ss_no_read.v @@ -0,0 +1,5 @@ +`define SPI_MASTER_SS_NAME spi_master_ss_no_read +`define SPI_MASTER_NAME spi_master_no_read +`define SPI_MASTER_NO_READ +/* verilator lint_off DECLFILENAME */ +`include "spi_master_ss_template.v" diff --git a/spi_master_ss_no_write.v b/spi_master_ss_no_write.v new file mode 100644 index 0000000..9be3e7f --- /dev/null +++ b/spi_master_ss_no_write.v @@ -0,0 +1,5 @@ +`define SPI_MASTER_SS_NAME spi_master_ss_no_write +`define SPI_MASTER_NAME spi_master_no_write +`define SPI_MASTER_NO_WRITE +/* verilator lint_off DECLFILENAME */ +`include "spi_master_ss_template.v" diff --git a/spi_master_ss_template.v b/spi_master_ss_template.v new file mode 100644 index 0000000..eeaf01c --- /dev/null +++ b/spi_master_ss_template.v @@ -0,0 +1,94 @@ +/* spi master with integrated ability to wait a certain amount of cycles + * after activating SS. + */ + +module `SPI_MASTER_SS_NAME +#( + parameter WID = 24, + parameter WID_LEN = 5, + parameter CYCLE_HALF_WAIT = 1, + parameter CYCLE_HALF_WAIT_TIMER_LEN = 3, + + parameter SS_WAIT = 1, + parameter SS_WAIT_TIMER_LEN = 2, + + parameter POLARITY = 0, + parameter PHASE = 0 +) +( + input clk, +`ifndef SPI_MASTER_NO_READ + output [WID-1:0] from_slave, + input miso, +`endif +`ifndef SPI_MASTER_NO_WRITE + input [WID-1:0] to_slave, + output reg mosi, +`endif + output sck_wire, + output finished, + output ss_L, + input arm +); + +reg ss = 0; +reg arm_master = 0; +assign ss_L = ss; + +`SPI_MASTER_NAME #( + .WID(WID), + .WID_LEN(WID_LEN), + .CYCLE_HALF_WAIT(CYCLE_HALF_WAIT), + .TIMER_LEN(CYCLE_HALF_WAIT_TIMER_LEN), + .POLARITY(POLARITY), + .PHASE(PHASE) +) master ( + .clk(clk), +`ifndef SPI_MASTER_NO_READ + .from_slave(from_slave), + .miso(miso), +`endif +`ifndef SPI_MASTER_NO_WRITE + .to_slave(to_slave), + .mosi(mosi), +`endif + .sck_wire(sck_wire), + .finished(finished), + .arm(arm_master) +); + +localparam WAIT_ON_ARM = 0; +localparam WAIT_ON_SS = 1; +localparam WAIT_ON_MASTER = 2; +localparam WAIT_ON_ARM_DEASSERT = 3; +reg [2:0] state = WAIT_ON_ARM; + +always @ (posedge clk) begin + case (state) + WAIT_ON_ARM: begin + if (arm) begin + timer <= 1; + state <= WAIT_ON_SS; + ss <= 1; + end + end + WAIT_ON_SS: begin + if (timer == SS_WAIT) begin + arm_master <= 1; + state <= WAIT_ON_MASTER; + end + end + WAIT_ON_MASTER: begin + if (finished) begin + state <= WAIT_ON_ARM_DEASSERT; + end + end + WAIT_ON_ARM_DEASSERT: begin + if (!arm) begin + state <= WAIT_ON_ARM; + arm_master <= 0; + end + end +end + +endmodule