Vast and substantial changes: Removed the old GPIO control with the new one
that implements a shift register around the perimeter of the chip, to control
most aspects of each GPIO pad locally to avoid excessive wiring. Added modules
for the metal-programmed user ID, two counter-timers, and a general-purpose SPI
master. The SPI master can be internally directly connected to the SPI slave,
so the processor can access the housekeeping SPI in the same way as an external
host. Most signals other than 1 GPIO pin and the SPI flash controller pins were
remapped to pads in the user area, where they are active on startup and until
they are programmed for user use from the management processor. There are
several known syntax issues that need to be fixed; this is a work in progress.
diff --git a/verilog/rtl/caravel_spi_slave.v b/verilog/rtl/caravel_spi_slave.v
new file mode 100644
index 0000000..e8a6494
--- /dev/null
+++ b/verilog/rtl/caravel_spi_slave.v
@@ -0,0 +1,221 @@
+//------------------------------------------------------
+// caravel_spi_slave.v
+//------------------------------------------------------
+// General purpose SPI slave module for the Caravel chip
+//------------------------------------------------------
+// Written by Tim Edwards
+// efabless, inc., September 28, 2020
+//------------------------------------------------
+// This file is distributed free and open source
+//------------------------------------------------
+
+// SCK --- Clock input
+// SDI --- Data input
+// SDO --- Data output
+// CSB --- Chip select (sense negative)
+// idata --- Data from chip to transmit out, in 8 bits
+// odata --- Input data to chip, in 8 bits
+// addr --- Decoded address to upstream circuits
+// rdstb --- Read strobe, tells upstream circuit to supply next byte to idata
+// wrstb --- Write strobe, tells upstream circuit to latch odata.
+
+// Data format (general purpose):
+// 8 bit format
+// 1st byte: Command word (see below)
+// 2nd byte: Address word (register 0 to 255)
+// 3rd byte: Data word (value 0 to 255)
+
+// Command format:
+// 00000000 No operation
+// 10000000 Write until CSB raised
+// 01000000 Read until CSB raised
+// 11000000 Simultaneous read/write until CSB raised
+// 11000100 Pass-through read/write to management area flash SPI until CSB raised
+// 11000101 Pass-through read/write to user area flash SPI until CSB raised
+// wrnnn000 Read/write as above, for nnn = 1 to 7 bytes, then terminate
+
+// Lower three bits are reserved for future use.
+// All serial bytes are read and written msb first.
+
+// Fixed control and status registers
+
+// Address 0 is reserved and contains flags for SPI mode. This is
+// currently undefined and is always value 0.
+// Address 1 is reserved and contains manufacturer ID low 8 bits.
+// Address 2 is reserved and contains manufacturer ID high 4 bits.
+// Address 3 is reserved and contains product ID (8 bits).
+// Addresses 4 to 7 are reserved and contain the mask ID (32 bits).
+// Addresses 8 to 255 are available for general purpose use.
+
+`define COMMAND 3'b000
+`define ADDRESS 3'b001
+`define DATA 3'b010
+`define USERPASS 3'b100
+`define MGMTPASS 3'b101
+
+module caravel_spi_slave(reset, SCK, SDI, CSB, SDO,
+ sdoenb, idata, odata, oaddr, rdstb, wrstb,
+ pass_thru_mgmt, pass_thru_mgmt_delay,
+ pass_thru_user, pass_thru_user_delay, pass_thru_reset);
+
+ input reset;
+ input SCK;
+ input SDI;
+ input CSB;
+ output SDO;
+ output sdoenb;
+ input [7:0] idata;
+ output [7:0] odata;
+ output [7:0] oaddr;
+ output rdstb;
+ output wrstb;
+ output pass_thru_mgmt;
+ output pass_thru_mgmt_delay;
+ output pass_thru_user;
+ output pass_thru_user_delay;
+ output pass_thru_reset;
+
+ reg [7:0] addr;
+ reg wrstb;
+ reg rdstb;
+ reg sdoenb;
+ reg [2:0] state;
+ reg [2:0] count;
+ reg writemode;
+ reg readmode;
+ reg [2:0] fixed;
+ wire [7:0] odata;
+ reg [6:0] predata;
+ wire [7:0] oaddr;
+ reg [7:0] ldata;
+ reg pass_thru_mgmt;
+ reg pass_thru_mgmt_delay;
+ reg pre_pass_thru_mgmt;
+ reg pass_thru_user;
+ reg pass_thru_user_delay;
+ reg pre_pass_thru_user;
+ wire csb_reset;
+
+ assign odata = {predata, SDI};
+ assign oaddr = (state == `ADDRESS) ? {addr[6:0], SDI} : addr;
+ assign SDO = ldata[7];
+ assign csb_reset = CSB | reset;
+ assign pass_thru_reset = pass_thru_mgmt_delay | pre_pass_thru_mgmt;
+
+ // Readback data is captured on the falling edge of SCK so that
+ // it is guaranteed valid at the next rising edge.
+ always @(negedge SCK or posedge CSB) begin
+ if (CSB == 1'b1) begin
+ wrstb <= 1'b0;
+ ldata <= 8'b00000000;
+ sdoenb <= 1'b1;
+ end else begin
+
+ // After CSB low, 1st SCK starts command
+
+ if (state == `DATA) begin
+ if (readmode == 1'b1) begin
+ sdoenb <= 1'b0;
+ if (count == 3'b000) begin
+ ldata <= idata;
+ end else begin
+ ldata <= {ldata[6:0], 1'b0}; // Shift out
+ end
+ end else begin
+ sdoenb <= 1'b1;
+ end
+
+ // Apply write strobe on SCK negative edge on the next-to-last
+ // data bit so that it updates data on the rising edge of SCK
+ // on the last data bit.
+
+ if (count == 3'b111) begin
+ if (writemode == 1'b1) begin
+ wrstb <= 1'b1;
+ end
+ end else begin
+ wrstb <= 1'b0;
+ end
+ end else if (state == `MGMTPASS || state == `USERPASS) begin
+ wrstb <= 1'b0;
+ sdoenb <= 1'b0;
+ end else begin
+ wrstb <= 1'b0;
+ sdoenb <= 1'b1;
+ end // ! state `DATA
+ end // ! CSB
+ end // always @ ~SCK
+
+ always @(posedge SCK or posedge CSB) begin
+ if (csb_reset == 1'b1) begin
+ // Default state on reset
+ addr <= 8'h00;
+ rdstb <= 1'b0;
+ predata <= 7'b0000000;
+ state <= `COMMAND;
+ count <= 3'b000;
+ readmode <= 1'b0;
+ writemode <= 1'b0;
+ fixed <= 3'b000;
+ pass_thru_mgmt <= 1'b0;
+ pass_thru_mgmt_delay <= 1'b0;
+ pre_pass_thru_mgmt <= 1'b0;
+ pass_thru_user = 1'b0;
+ pass_thru_user_delay <= 1'b0;
+ pre_pass_thru_user <= 1'b0;
+ end else begin
+ // After CSB low, 1st SCK starts command
+ if (state == `COMMAND) begin
+ rdstb <= 1'b0;
+ count <= count + 1;
+ if (count == 3'b000) begin
+ writemode <= SDI;
+ end else if (count == 3'b001) begin
+ readmode <= SDI;
+ end else if (count < 3'b101) begin
+ fixed <= {fixed[1:0], SDI};
+ end else if (count < 3'b110) begin
+ pass_thru_mgmt_delay <= pre_pass_thru_mgmt;
+ pass_thru_user_delay <= pre_pass_thru_user;
+ end else if (count == 3'b111) begin
+ if (pre_pass_thru_mgmt == 1'b1) begin
+ state <= `MGMTPASS;
+ pre_pass_thru_mgmt <= 1'b0;
+ end else if (pre_pass_thru_user == 1'b1) begin
+ state <= `USERPASS;
+ pre_pass_thru_user <= 1'b0;
+ end else begin
+ state <= `ADDRESS;
+ end
+ end
+ end else if (state == `ADDRESS) begin
+ count <= count + 1;
+ addr <= {addr[6:0], SDI};
+ if (count == 3'b111) begin
+ if (readmode == 1'b1) begin
+ rdstb <= 1'b1;
+ end
+ state <= `DATA;
+ end else begin
+ rdstb <= 1'b0;
+ end
+ end else if (state == `DATA) begin
+ predata <= {predata[6:0], SDI};
+ count <= count + 1;
+ if (count == 3'b111) begin
+ if (fixed == 3'b001) begin
+ state <= `COMMAND;
+ end else if (fixed != 3'b000) begin
+ fixed <= fixed - 1;
+ addr <= addr + 1; // Auto increment address (fixed)
+ end else begin
+ addr <= addr + 1; // Auto increment address (streaming)
+ end
+ end else begin
+ rdstb <= 1'b0;
+ end
+ end // ! state `DATA
+ end // ! CSB
+ end // always @ SCK
+
+endmodule // caravel_spi_slave