import spi v0.2

This commit is contained in:
Peter McGoron 2022-11-14 08:43:16 -05:00
parent 50ea679e02
commit 0907a76c22
7 changed files with 190 additions and 22 deletions

View File

@ -1,5 +1,12 @@
/* SPI master.
* Written by Peter McGoron, 2022.
/* (c) Peter McGoron 2022 v0.2
* 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/.
*/
/* CYCLE_HALF_WAIT should take into account the setup time of the slave
* device, and also master buffering (MISO is one cycle off to stabilize
* the input).
*/
module
@ -12,11 +19,11 @@ spi_master_no_write
spi_master
`endif
`endif
#(
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, // Half of the wait time of a cycle minus 1.
// One SCK cycle 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.
@ -29,13 +36,30 @@ spi_master
`endif
`ifndef SPI_MASTER_NO_WRITE
input [WID-1:0] to_slave,
output mosi,
output reg mosi,
`endif
output sck_wire,
output finished,
output reg sck_wire,
output reg finished,
input arm
);
`ifndef SPI_MASTER_NO_READ
/* MISO is almost always an external wire, so buffer it.
* This might not be necessary, since the master and slave do not respond
* immediately to changes in the wires, but this is just to be safe.
* It is trivial to change, just do
* wire read_miso = miso;
*/
reg miso_hot = 0;
reg read_miso = 0;
always @ (posedge clk) begin
read_miso <= miso_hot;
miso_hot <= miso;
end
`endif
parameter WAIT_ON_ARM = 0;
parameter ON_CYCLE = 1;
parameter CYCLE_WAIT = 2;
@ -68,7 +92,7 @@ endtask
task read_data();
`ifndef SPI_MASTER_NO_READ
from_slave <= from_slave << 1;
from_slave[0] <= miso;
from_slave[0] <= read_miso;
`endif
endtask
@ -100,6 +124,16 @@ task setup_bits();
end
endtask
task cycle_change();
// Stop transfer when the clock returns to its original polarity.
if (bit_counter == WID[WID_LEN-1:0] && sck == POLARITY[0]) begin
state <= WAIT_FINISHED;
end else begin
sck <= !sck;
state <= ON_CYCLE;
end
endtask
always @ (posedge clk) begin
case (state)
WAIT_ON_ARM: begin
@ -132,19 +166,17 @@ always @ (posedge clk) begin
bit_counter <= bit_counter + 1;
end
end
state <= CYCLE_WAIT;
if (CYCLE_HALF_WAIT == 0) begin
cycle_change();
end else begin
state <= CYCLE_WAIT;
end
end
CYCLE_WAIT: begin
if (timer == CYCLE_HALF_WAIT) begin
timer <= 0;
// Stop transfer when the clock returns
// to its original polarity.
if (bit_counter == WID && sck == POLARITY) begin
state <= WAIT_FINISHED;
end else begin
state <= ON_CYCLE;
sck <= !sck;
end
timer <= 1;
cycle_change();
end else begin
timer <= timer + 1;
end

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,108 @@
/* 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 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(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;
reg [SS_WAIT_TIMER_LEN-1:0] timer = 0;
task master_arm();
arm_master <= 1;
state <= WAIT_ON_MASTER;
endtask
always @ (posedge clk) begin
case (state)
WAIT_ON_ARM: begin
if (arm) begin
timer <= 1;
if (SS_WAIT == 0) begin
master_arm();
end else begin
timer <= 1;
state <= WAIT_ON_SS;
end
ss <= 1;
end
end
WAIT_ON_SS: begin
if (timer == SS_WAIT) begin
master_arm();
end else begin
timer <= timer + 1;
end
end
WAIT_ON_MASTER: begin
if (finished) begin
state <= WAIT_ON_ARM_DEASSERT;
ss <= 0;
end
end
WAIT_ON_ARM_DEASSERT: begin
if (!arm) begin
state <= WAIT_ON_ARM;
arm_master <= 0;
end
end
endcase
end
endmodule

View File

@ -1,4 +1,4 @@
/* (c) Peter McGoron 2022 v0.1
/* (c) Peter McGoron 2022 v0.2
* 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/.
@ -24,7 +24,7 @@ spi_slave
input ss_L,
`ifndef SPI_SLAVE_NO_READ
output reg [WID-1:0] from_master,
input reg mosi,
input mosi,
`endif
`ifndef SPI_SLAVE_NO_WRITE
input [WID-1:0] to_master,
@ -35,6 +35,17 @@ spi_slave
output reg err
);
`ifndef SPI_SLAVE_NO_READ
/* MOSI is almost always an external wire, so buffer it. */
reg mosi_hot = 0;
reg read_mosi = 0;
always @ (posedge clk) begin
read_mosi <= mosi_hot;
mosi_hot <= mosi;
end
`endif
wire ss = !ss_L;
reg sck_delay = 0;
reg [WID_LEN-1:0] bit_counter = 0;
@ -48,7 +59,7 @@ reg [WID-1:0] send_buf = 0;
task read_data();
`ifndef SPI_SLAVE_NO_READ
from_master <= from_master << 1;
from_master[0] <= mosi;
from_master[0] <= read_mosi;
`endif
endtask
@ -77,7 +88,7 @@ task setup_bits();
endtask
task check_counter();
if (bit_counter == WID) begin
if (bit_counter == WID[WID_LEN-1:0]) begin
err <= ready_at_start;
end else begin
bit_counter <= bit_counter + 1;

View File

@ -0,0 +1,3 @@
`define SPI_SLAVE_NO_READ
/* verilator lint_off DECLFILENAME */
`include "spi_slave.v"