add verilog SPI

This commit is contained in:
Peter McGoron 2022-07-21 17:07:52 -04:00
parent 38c15cec45
commit 01cbcb5fae
3 changed files with 227 additions and 23 deletions

View File

@ -0,0 +1,164 @@
/* (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/.
*/
module
`ifdef SPI_MASTER_NO_READ
spi_master_no_read
`else
`ifdef SPI_MASTER_NO_WRITE
spi_master_no_write
`else
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 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.
)
(
input clk,
`ifndef SPI_MASTER_NO_READ
output reg [WID-1:0] from_slave,
input miso,
`endif
`ifndef SPI_MASTER_NO_WRITE
input [WID-1:0] to_slave,
output mosi,
`endif
output sck_wire,
output finished,
input arm
);
parameter WAIT_ON_ARM = 0;
parameter ON_CYCLE = 1;
parameter CYCLE_WAIT = 2;
parameter WAIT_FINISHED = 3;
reg [1:0] state = WAIT_ON_ARM;
reg [WID_LEN-1:0] bit_counter = 0;
reg [TIMER_LEN-1:0] timer = 0;
`ifndef SPI_MASTER_NO_WRITE
reg [WID-1:0] send_buf = 0;
`endif
reg sck = 0;
assign sck_wire = sck;
task idle_state();
if (POLARITY == 0) begin
sck <= 0;
end else begin
sck <= 1;
end
`ifndef SPI_MASTER_NO_WRITE
mosi <= 0;
`endif
timer <= 0;
bit_counter <= 0;
endtask
task read_data();
`ifndef SPI_MASTER_NO_READ
from_slave <= from_slave << 1;
from_slave[0] <= miso;
`endif
endtask
task write_data();
`ifndef SPI_MASTER_NO_WRITE
mosi <= send_buf[WID-1];
send_buf <= send_buf << 1;
`endif
endtask
task setup_bits();
/* 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.
*
* For mode 01 and mode 10, the first action is a WRITE.
*/
if (POLARITY == PHASE) begin
`ifndef SPI_MASTER_NO_WRITE
mosi <= to_slave[WID-1];
send_buf <= to_slave << 1;
`endif
state <= CYCLE_WAIT;
end else begin
`ifndef SPI_MASTER_NO_WRITE
send_buf <= to_slave;
`endif
state <= ON_CYCLE;
end
endtask
always @ (posedge clk) begin
case (state)
WAIT_ON_ARM: begin
if (!arm) begin
idle_state();
finished <= 0;
end else begin
setup_bits();
end
end
ON_CYCLE: begin
if (sck) begin // rising edge
if (PHASE == 1) begin
write_data();
end else begin
read_data();
end
if (POLARITY == 0) begin
bit_counter <= bit_counter + 1;
end
end else begin // falling edge
if (PHASE == 1) begin
read_data();
end else begin
write_data();
end
if (POLARITY == 1) begin
bit_counter <= bit_counter + 1;
end
end
state <= CYCLE_WAIT;
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
end else begin
timer <= timer + 1;
end
end
WAIT_FINISHED: begin
finished <= 1;
idle_state();
if (!arm) begin
state <= WAIT_ON_ARM;
end
end
endcase
end
endmodule

View File

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

View File

@ -139,31 +139,66 @@ io = [
IOStandard("LVCMOS33")) IOStandard("LVCMOS33"))
] ]
class DACThroughGPIO(Module, AutoCSR): class SPIMaster(Module, AutoCSR):
def __init__(self, pins): def __init__(self, wid, clk, pins):
self._miso = CSRStatus(1, description="Master In, Slave Out (Status)") self.pins = pins
# Read as [MSB ... LSB]
self._ctrl = CSRStorage(3, description="SS, SCK, MOSI (Control)")
self._pins = pins
self.comb += self._miso.status.eq(self._pins.miso) self.from_slave = CSRStatus(wid, description="Data from slave (Status)")
self.to_slave = CSRStorage(wid, description="Data to slave (Control)")
self.finished = CSRStatus(1, description="Finished transmission (Status)")
self.arm = CSRStorage(1, description="Initiate transmission (Status)")
self.ss = CSRStorage(1, description="Slave Select (active high)")
self.comb += self._pins.ss.eq(~self._ctrl.storage[2]) self.comb += self.pins.ss.eq(~self.ss.storage)
self.comb += self._pins.mosi.eq(self._ctrl.storage[1])
self.comb += self._pins.sck.eq(self._ctrl.storage[0])
class ADCThroughGPIO(Module, AutoCSR): import math
def __init__(self, pins):
self._pins = pins
self._conv = CSRStorage(1, description="Conversion Signal (Control)")
self._sck = CSRStorage(1, description="Serial Clock (Control)")
self._sdo = CSRStatus(1, description="Serial Data Output (Status)")
self.comb += self._pins.conv.eq(self._conv.storage) self.specials += Instance("spi_master",
self.comb += self._pins.sck.eq(self._sck.storage) p_WID=wid,
p_WID_LEN=math.ceil(math.log2(wid)),
p_CYCLE_HALF_WAIT = 3, # 3 + 2 = 5, total sck = 10 cycles
p_TIMER_LEN = 3,
p_POLARITY = 0,
p_PHASE = 1,
i_clk = clk,
o_from_slave = self.from_slave.status,
i_miso = self.pins.miso,
i_to_slave = self.to_slave.storage,
o_mosi = self.pins.mosi,
o_sck_wire = self.pins.sck,
o_finished = self.finished.status,
i_arm = self.arm.storage
)
self.comb += self._sdo.status.eq(self._pins.sdo) class SPIMasterReadOnly(Module, AutoCSR):
def __init__(self, wid, clk, pins):
self.pins = pins
self.from_slave = CSRStatus(wid, description="Data from slave (Status)description=")
self.finished = CSRStatus(1, description="Finished transmission (Status)description=")
self.arm = CSRStorage(1, description="Initiate transmission (Status)description=")
self.conv = CSRStorage(1, description="Conversion (active high)description=")
self.comb += self.pins.conv.eq(self.conv.storage)
import math
self.specials += Instance("spi_master_no_write",
p_WID=wid,
p_WID_LEN=math.ceil(math.log2(wid)),
p_CYCLE_HALF_WAIT = 1, # 1 + 2 = 3, total sck = 6 cycles
p_TIMER_LEN = 3,
p_POLARITY = 1,
p_PHASE = 0,
i_clk = clk,
o_from_slave = self.from_slave.status,
i_miso = self.pins.sdo,
o_sck_wire = self.pins.sck,
o_finished = self.finished.status,
i_arm = self.arm.storage
)
# Clock and Reset Generator
class _CRG(Module): class _CRG(Module):
def __init__(self, platform, sys_clk_freq, with_dram=True, with_rst=True): def __init__(self, platform, sys_clk_freq, with_dram=True, with_rst=True):
self.rst = Signal() self.rst = Signal()
@ -200,6 +235,8 @@ class CryoSNOM1SoC(SoCCore):
sys_clk_freq = int(100e6) sys_clk_freq = int(100e6)
platform = board_spec.Platform(variant=variant, toolchain="symbiflow") platform = board_spec.Platform(variant=variant, toolchain="symbiflow")
self.submodules.crg = _CRG(platform, sys_clk_freq, True) self.submodules.crg = _CRG(platform, sys_clk_freq, True)
platform.add_source("rtl/spi/spi_master.v")
platform.add_source("rtl/spi/spi_master_no_write.v")
# SoCCore does not have sane defaults (no integrated rom) # SoCCore does not have sane defaults (no integrated rom)
SoCCore.__init__(self, SoCCore.__init__(self,
@ -230,16 +267,16 @@ class CryoSNOM1SoC(SoCCore):
l2_cache_size = 8192 l2_cache_size = 8192
) )
self.submodules.ethphy = LiteEthPHYMII( self.submodules.ethphy = LiteEthPHYMII(
clock_pads = self.platform.request("eth_clocks"), clock_pads = platform.request("eth_clocks"),
pads = self.platform.request("eth")) pads = platform.request("eth"))
self.add_ethernet(phy=self.ethphy, dynamic_ip=True) self.add_ethernet(phy=self.ethphy, dynamic_ip=True)
# Add the DAC and ADC pins as GPIO. They will be used directly # Add the DAC and ADC pins as GPIO. They will be used directly
# by Zephyr. # by Zephyr.
platform.add_extension(io) platform.add_extension(io)
for i in range(0,8): for i in range(0,8):
setattr(self.submodules, f"dac{i}", DACThroughGPIO(platform.request("dac", i))) setattr(self.submodules, f"dac{i}", SPIMaster(24, ClockSignal(), platform.request("dac", i)))
setattr(self.submodules, f"adc{i}", ADCThroughGPIO(platform.request("adc", i))) setattr(self.submodules, f"adc{i}", SPIMasterReadOnly(24, ClockSignal(), platform.request("adc", i)))
def main(): def main():
soc = CryoSNOM1SoC("a7-35") soc = CryoSNOM1SoC("a7-35")