add verilog SPI
This commit is contained in:
parent
38c15cec45
commit
01cbcb5fae
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
`define SPI_MASTER_NO_WRITE
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_master.v"
|
|
@ -139,31 +139,66 @@ io = [
|
|||
IOStandard("LVCMOS33"))
|
||||
]
|
||||
|
||||
class DACThroughGPIO(Module, AutoCSR):
|
||||
def __init__(self, pins):
|
||||
self._miso = CSRStatus(1, description="Master In, Slave Out (Status)")
|
||||
# Read as [MSB ... LSB]
|
||||
self._ctrl = CSRStorage(3, description="SS, SCK, MOSI (Control)")
|
||||
self._pins = pins
|
||||
class SPIMaster(Module, AutoCSR):
|
||||
def __init__(self, wid, clk, pins):
|
||||
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.mosi.eq(self._ctrl.storage[1])
|
||||
self.comb += self._pins.sck.eq(self._ctrl.storage[0])
|
||||
self.comb += self.pins.ss.eq(~self.ss.storage)
|
||||
|
||||
class ADCThroughGPIO(Module, AutoCSR):
|
||||
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)")
|
||||
import math
|
||||
|
||||
self.comb += self._pins.conv.eq(self._conv.storage)
|
||||
self.comb += self._pins.sck.eq(self._sck.storage)
|
||||
self.specials += Instance("spi_master",
|
||||
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):
|
||||
def __init__(self, platform, sys_clk_freq, with_dram=True, with_rst=True):
|
||||
self.rst = Signal()
|
||||
|
@ -200,6 +235,8 @@ class CryoSNOM1SoC(SoCCore):
|
|||
sys_clk_freq = int(100e6)
|
||||
platform = board_spec.Platform(variant=variant, toolchain="symbiflow")
|
||||
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.__init__(self,
|
||||
|
@ -230,16 +267,16 @@ class CryoSNOM1SoC(SoCCore):
|
|||
l2_cache_size = 8192
|
||||
)
|
||||
self.submodules.ethphy = LiteEthPHYMII(
|
||||
clock_pads = self.platform.request("eth_clocks"),
|
||||
pads = self.platform.request("eth"))
|
||||
clock_pads = platform.request("eth_clocks"),
|
||||
pads = platform.request("eth"))
|
||||
self.add_ethernet(phy=self.ethphy, dynamic_ip=True)
|
||||
|
||||
# Add the DAC and ADC pins as GPIO. They will be used directly
|
||||
# by Zephyr.
|
||||
platform.add_extension(io)
|
||||
for i in range(0,8):
|
||||
setattr(self.submodules, f"dac{i}", DACThroughGPIO(platform.request("dac", i)))
|
||||
setattr(self.submodules, f"adc{i}", ADCThroughGPIO(platform.request("adc", i)))
|
||||
setattr(self.submodules, f"dac{i}", SPIMaster(24, ClockSignal(), platform.request("dac", i)))
|
||||
setattr(self.submodules, f"adc{i}", SPIMasterReadOnly(24, ClockSignal(), platform.request("adc", i)))
|
||||
|
||||
def main():
|
||||
soc = CryoSNOM1SoC("a7-35")
|
||||
|
|
Loading…
Reference in New Issue