├── README ├── lbus_regmap.v ├── spi_regmap_top.v ├── spi_regmap_write.png ├── spi_slave_lbus.v └── spi_slave_memory.py /README: -------------------------------------------------------------------------------- 1 | This is a Verilog 2001 RTL design that implementes a (1) SPI Slave and (2) Flipflop register memory map. 2 | Why is it in Verilog 2001? Because Xilinx Vivado (free version) only supports VHDL and Verilog 2001. 3 | I have tested this design in a CMOD_A7 device connected to a Raspberry Pi 3. 4 | The SPI Slave is important as it allows high-speed communication between the FPGA and the Raspberry Pi. 5 | My first project is to drive JTAG scan patterns to an ASIC at the same speed the ATE tester does. 6 | After that I want to be able to record multiple signals and save the pattern to a file on the Raspberry Pi. 7 | 8 | Files: 9 | (1) spi_slave_lbus.v 10 | -> This file implementents a SPI slave and has a parallel "Local Bus" interface. 11 | (2) lbus_regmap.v 12 | -> This file implements a "Local Bus" register memory map. 2048 x 8 flipflops that are writeable and readable in byte width. 13 | (3) spi_regmap_top.v 14 | -> This file is the top-level that contains the spi_slave_lbus and lbus_regmap instances. 15 | (4) spi_slave_memory.py 16 | -> This file reads and writes byte data to the FPGA from the Raspberry Pi 17 | -------------------------------------------------------------------------------- /lbus_regmap.v: -------------------------------------------------------------------------------- 1 | 2 | module lbus_regmap 3 | ( input clk, 4 | input rst_n, 5 | input rd_en_sclk, 6 | input wr_en_sclk, 7 | input [15:0] address_sclk, 8 | input [7:0] wdata_sclk, 9 | output reg [7:0] rdata 10 | ); 11 | 12 | reg sync_rd_en_ff1; 13 | reg sync_rd_en_ff2; 14 | reg sync_wr_en_ff1; 15 | reg sync_wr_en_ff2; 16 | reg hold_sync_wr_en_ff2; 17 | 18 | wire [10:0] raddress; 19 | 20 | reg [7:0] registers[2047:0]; 21 | 22 | always @(posedge clk or negedge rst_n) 23 | if (~rst_n) sync_rd_en_ff1 <= 1'b0; 24 | else sync_rd_en_ff1 <= rd_en_sclk; 25 | 26 | always @(posedge clk or negedge rst_n) 27 | if (~rst_n) sync_rd_en_ff2 <= 1'b0; 28 | else sync_rd_en_ff2 <= sync_rd_en_ff1; 29 | 30 | always @(posedge clk or negedge rst_n) 31 | if (~rst_n) sync_wr_en_ff1 <= 1'b0; 32 | else sync_wr_en_ff1 <= wr_en_sclk; 33 | 34 | always @(posedge clk or negedge rst_n) 35 | if (~rst_n) sync_wr_en_ff2 <= 1'b0; 36 | else sync_wr_en_ff2 <= sync_wr_en_ff1; 37 | 38 | always @(posedge clk or negedge rst_n) 39 | if (~rst_n) hold_sync_wr_en_ff2 <= 1'b0; 40 | else hold_sync_wr_en_ff2 <= sync_wr_en_ff2; 41 | 42 | assign raddress = {11{sync_rd_en_ff2}} & address_sclk[10:0]; 43 | 44 | always @* 45 | rdata[7:0] = (|address_sclk[15:11]) ? 8'd0 : registers[raddress]; 46 | 47 | integer i; 48 | 49 | always @(posedge clk or negedge rst_n) 50 | if (~rst_n) for (i=0; i<2048; i=i+1) registers[i] <= 8'h00; 51 | else if ((sync_wr_en_ff2 == 1'b1) && (hold_sync_wr_en_ff2 == 1'b0)) registers[address_sclk[10:0]] <= wdata_sclk; 52 | endmodule -------------------------------------------------------------------------------- /spi_regmap_top.v: -------------------------------------------------------------------------------- 1 | 2 | module spi_regmap_top 3 | ( input clk, // 12MHz clock 4 | input reset, // button 5 | input sclk, // SPI CLK 6 | input ss_n, // SPI CS_N 7 | input mosi, // SPI MOSI 8 | output wire miso // SPI MISO 9 | ); 10 | 11 | wire [7:0] rdata; 12 | wire [7:0] wdata; 13 | wire [15:0] address; 14 | wire rd_en; 15 | wire wr_en; 16 | wire rst_n; 17 | wire reset_spi; 18 | 19 | assign rst_n = ~reset; 20 | 21 | assign reset_spi = reset || ss_n; // clear the SPI when the chip_select is inactive 22 | 23 | spi_slave_lbus u_spi_slave_lbus 24 | ( .sclk, // input 25 | .mosi, // input 26 | .miso, // output 27 | .reset_spi, // input 28 | .rdata, // input [7:0] 29 | .rd_en, // output 30 | .wr_en, // output 31 | .wdata, // output [7:0] 32 | .address // output [15:0] 33 | ); 34 | 35 | lbus_regmap u_lbus_regmap 36 | ( .clk, // input 37 | .rst_n, // input 38 | .rd_en_sclk (rd_en), // input 39 | .wr_en_sclk (wr_en), // input 40 | .address_sclk (address), // input [15:0] 41 | .wdata_sclk (wdata), // input [7:0] 42 | .rdata // output [7:0] 43 | ); 44 | 45 | endmodule -------------------------------------------------------------------------------- /spi_regmap_write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charkster/spi_slave_verilog/927f7e4690a16ad771b530968dcc2d7cd8583851/spi_regmap_write.png -------------------------------------------------------------------------------- /spi_slave_lbus.v: -------------------------------------------------------------------------------- 1 | 2 | // CPOL == 1'b0, CPHA = 1'b0, why would anyone do anything else? 3 | 4 | module spi_slave_lbus 5 | ( input sclk, // SPI 6 | input mosi, // SPI 7 | output reg miso, // SPI 8 | input reset_spi, // ASYNC_RESET 9 | input [7:0] rdata, // LBUS 10 | output reg rd_en, // LBUS 11 | output reg wr_en, // LBUS 12 | output reg [7:0] wdata, // LBUS 13 | output reg [15:0] address // LBUS 14 | ); 15 | 16 | reg [6:0] mosi_buffer; 17 | reg [5:0] bit_count; 18 | reg read_cycle; 19 | reg write_cycle; 20 | reg multi_cycle; 21 | 22 | always @(posedge sclk or posedge reset_spi) 23 | if (reset_spi) mosi_buffer <= 7'd0; 24 | else mosi_buffer <= {mosi_buffer[5:0],mosi}; 25 | 26 | always @(posedge sclk or posedge reset_spi) 27 | if (reset_spi) bit_count <= 6'd0; 28 | else if ((read_cycle == 1'b1) && (bit_count == 6'd31)) bit_count <= 6'd24; 29 | else if ((write_cycle == 1'b1) && (bit_count == 6'd32)) bit_count <= 6'd25; 30 | else bit_count <= bit_count + 1; 31 | 32 | always @(negedge sclk or posedge reset_spi) 33 | if (reset_spi) miso <= 1'b0; 34 | else if (bit_count < 6'd24) miso <= 1'b0; 35 | else if (rd_en == 1'b1) miso <= rdata[6'd31 - bit_count]; 36 | else miso <= 1'b0; 37 | 38 | // read command is 8'b0000_0010, write command is 8'b0000_0001 39 | 40 | always @(posedge sclk or posedge reset_spi) 41 | if (reset_spi) read_cycle <= 1'b0; 42 | else if ((bit_count == 6'd7) && (mosi_buffer == 7'h01) && (mosi == 1'b0)) read_cycle <= 1'b1; 43 | 44 | always @(posedge sclk or posedge reset_spi) 45 | if (reset_spi) rd_en <= 1'b0; 46 | else if ((read_cycle == 1'b1) && (bit_count >= 6'd23)) rd_en <= 1'b1; 47 | else rd_en <= 1'b0; 48 | 49 | always @(posedge sclk or posedge reset_spi) 50 | if (reset_spi) write_cycle <= 1'b0; 51 | else if ((bit_count == 6'd7) && (mosi_buffer == 7'h00) && (mosi == 1'b1)) write_cycle <= 1'b1; 52 | 53 | always @(posedge sclk or posedge reset_spi) 54 | if (reset_spi) wr_en <= 1'b0; 55 | else if ((write_cycle == 1'b1) && (bit_count == 6'd31)) wr_en <= 1'b1; 56 | else wr_en <= 1'b0; 57 | 58 | always @(posedge sclk or posedge reset_spi) 59 | if (reset_spi) address[15:0] <= 16'h0000; 60 | else if ((read_cycle || write_cycle) && (bit_count == 6'd15)) address[15:8] <= {mosi_buffer[6:0],mosi}; 61 | else if ((read_cycle || write_cycle) && (bit_count == 6'd23)) address[7:0] <= {mosi_buffer[6:0],mosi}; 62 | else if ( read_cycle && (bit_count == 6'd31)) address[15:0] <= address[15:0] + 1; 63 | else if ( write_cycle && (bit_count == 6'd32)) address[15:0] <= address[15:0] + 1; 64 | 65 | always @(posedge sclk or posedge reset_spi) 66 | if (reset_spi) wdata <= 8'h00; 67 | else if (write_cycle && (bit_count == 6'd31)) wdata <= {mosi_buffer[6:0],mosi}; 68 | 69 | endmodule 70 | -------------------------------------------------------------------------------- /spi_slave_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import spidev 4 | import time 5 | 6 | class spi_slave_memory : 7 | spi = None 8 | 9 | _MAX_ADDRESS = 0xFFF 10 | _READ_COMMAND = 0x02 11 | _WRITE_COMMAND = 0x01 12 | 13 | # Constructor 14 | def __init__(self, spi_device=0, spi_channel=0, max_speed_hz = 5000000, mode = 0b00, debug=False): # 5MHz 15 | self.spi = spidev.SpiDev(spi_device, spi_channel) 16 | self.spi.max_speed_hz = max_speed_hz 17 | self.spi.mode = mode # CPOL,CPHA 18 | self.debug = debug 19 | 20 | def read_bytes(self, start_address=0x0000, num_bytes=1): 21 | if (self.debug == True): 22 | print "Called read_bytes" 23 | if (num_bytes == 0): 24 | print "Error: num_bytes must be larger than zero" 25 | return [] 26 | else: 27 | byte0 = self._READ_COMMAND 28 | byte1 = (start_address & 0xFF00) >> 8 29 | byte2 = start_address & 0xFF 30 | filler_bytes = [0x00] * int(num_bytes) 31 | read_list = self.spi.xfer2([byte0,byte1,byte2] + filler_bytes) 32 | read_list[0:3] = [] 33 | if (self.debug == True): 34 | address = start_address 35 | for read_byte in read_list: 36 | print "Address 0x%04x Read data 0x%02x" % (address,read_byte) 37 | address += 1 38 | return read_list 39 | 40 | def write_bytes(self, start_address=0x0000, write_byte_list=[]): 41 | byte0 = self._WRITE_COMMAND 42 | byte1 = (start_address & 0xFF00) >> 8 43 | byte2 = start_address & 0xFF 44 | self.spi.xfer2([byte0,byte1,byte2] + write_byte_list) 45 | if (self.debug == True): 46 | print "Called write_bytes" 47 | address = start_address 48 | for write_byte in write_byte_list: 49 | print "Wrote address 0x%04x data 0x%02x" % (address,write_byte) 50 | address += 1 51 | return 1 52 | 53 | 54 | mem = spi_slave_memory(debug=True) 55 | print "Write single address" 56 | mem.write_bytes(start_address=0x000F,write_byte_list=[0xe1]) 57 | print "Read single address" 58 | mem.read_bytes(start_address=0x000F,num_bytes=1) 59 | print "Write multiple addresses" 60 | mem.write_bytes(start_address=0x0005,write_byte_list=[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]) 61 | print "Read multiple addresses" 62 | mem.read_bytes(start_address=0x0001,num_bytes=16) 63 | --------------------------------------------------------------------------------