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"))
|
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")
|
||||||
|
|
Loading…
Reference in New Issue