Skip to content

Commit

Permalink
[hw,spi_host,rtl] Add a 32-bit command length register
Browse files Browse the repository at this point in the history
Currently the SPI host only supports a 9-bit command length, meaning a SPI
transaction can only transfer 512b. This especially problematic, for integrated
devices, where large FW blobs (>5MB) need to be fetched from the external SPI
flash. When using in conjunction with the DMA (hardware-handshake mode), only
a few chunks can be transferred before needing to re-program the SPI host and
DMA.

To overcome this problem, move the command length to a separate register and
increase the size up to 32-bit. This allows a more flexible use case and
allows the system to read large FW blobs with a single SPI and DMA
configuration.

Signed-off-by: Robert Schilling <[email protected]>
  • Loading branch information
Razer6 committed Nov 19, 2024
1 parent a7fb69b commit 2d52326
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 115 deletions.
22 changes: 17 additions & 5 deletions hw/ip/spi_host/data/spi_host.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,22 @@
the device.''',
resval: "0x0"
},
{ bits: "8:0",
],
tags: [// Triggers exceptions if registers are improperly configured
// Exclude from RW tests
"excl:CsrAllTests:CsrExclWrite"]
},
{ name: "COMMAND_LEN",
desc: '''Command length Register for a single command segment.

Parameters specific to each command segment. Unlike the !!CONFIGOPTS multi-register,
there is only one command length register for controlling all attached SPI devices''',
swaccess: "wo",
hwaccess: "hro",
hwext: "true",
hwqe: "true",
fields: [
{ bits: "31:0",
name: "LEN",
desc: '''Segment Length.

Expand All @@ -460,10 +475,7 @@
resval: "0x0"
},
],
tags: [// Triggers exceptions if registers are improperly configured
// Exclude from RW tests
"excl:CsrAllTests:CsrExclWrite"]
},
}
{ window: {
name: "RXDATA",
items: "1",
Expand Down
48 changes: 34 additions & 14 deletions hw/ip/spi_host/doc/registers.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
| spi_host.[`CONFIGOPTS`](#configopts) | 0x18 | 4 | Configuration options register. |
| spi_host.[`CSID`](#csid) | 0x1c | 4 | Chip-Select ID |
| spi_host.[`COMMAND`](#command) | 0x20 | 4 | Command Register |
| spi_host.[`RXDATA`](#rxdata) | 0x24 | 4 | SPI Receive Data. |
| spi_host.[`TXDATA`](#txdata) | 0x28 | 4 | SPI Transmit Data. |
| spi_host.[`ERROR_ENABLE`](#error_enable) | 0x2c | 4 | Controls which classes of errors raise an interrupt. |
| spi_host.[`ERROR_STATUS`](#error_status) | 0x30 | 4 | Indicates that any errors that have occurred. |
| spi_host.[`EVENT_ENABLE`](#event_enable) | 0x34 | 4 | Controls which classes of SPI events raise an interrupt. |
| spi_host.[`COMMAND_LEN`](#command_len) | 0x24 | 4 | Command length Register for a single command segment. |
| spi_host.[`RXDATA`](#rxdata) | 0x28 | 4 | SPI Receive Data. |
| spi_host.[`TXDATA`](#txdata) | 0x2c | 4 | SPI Transmit Data. |
| spi_host.[`ERROR_ENABLE`](#error_enable) | 0x30 | 4 | Controls which classes of errors raise an interrupt. |
| spi_host.[`ERROR_STATUS`](#error_status) | 0x34 | 4 | Indicates that any errors that have occurred. |
| spi_host.[`EVENT_ENABLE`](#event_enable) | 0x38 | 4 | Controls which classes of SPI events raise an interrupt. |

## INTR_STATE
Interrupt State Register
Expand Down Expand Up @@ -276,12 +277,12 @@ Command Register
there is only one command register for controlling all attached SPI devices
- Offset: `0x20`
- Reset default: `0x0`
- Reset mask: `0x3fff`
- Reset mask: `0x3e00`

### Fields

```wavejson
{"reg": [{"name": "LEN", "bits": 9, "attr": ["wo"], "rotate": 0}, {"name": "CSAAT", "bits": 1, "attr": ["wo"], "rotate": -90}, {"name": "SPEED", "bits": 2, "attr": ["wo"], "rotate": -90}, {"name": "DIRECTION", "bits": 2, "attr": ["wo"], "rotate": -90}, {"bits": 18}], "config": {"lanes": 1, "fontsize": 10, "vspace": 110}}
{"reg": [{"bits": 9}, {"name": "CSAAT", "bits": 1, "attr": ["wo"], "rotate": -90}, {"name": "SPEED", "bits": 2, "attr": ["wo"], "rotate": -90}, {"name": "DIRECTION", "bits": 2, "attr": ["wo"], "rotate": -90}, {"bits": 18}], "config": {"lanes": 1, "fontsize": 10, "vspace": 110}}
```

| Bits | Type | Reset | Name |
Expand All @@ -290,7 +291,7 @@ Command Register
| 13:12 | wo | 0x0 | [DIRECTION](#command--direction) |
| 11:10 | wo | 0x0 | [SPEED](#command--speed) |
| 9 | wo | 0x0 | [CSAAT](#command--csaat) |
| 8:0 | wo | 0x0 | [LEN](#command--len) |
| 8:0 | | | Reserved |

### COMMAND . DIRECTION
The direction for the following command: "0" = Dummy cycles
Expand All @@ -312,7 +313,26 @@ The speed for this command segment: "0" = Standard SPI. "1" = Dual SPI.
pausing for dummy cycles, and transmitting or receiving data from
the device.

### COMMAND . LEN
## COMMAND_LEN
Command length Register for a single command segment.

Parameters specific to each command segment. Unlike the [`CONFIGOPTS`](#configopts) multi-register,
there is only one command length register for controlling all attached SPI devices
- Offset: `0x24`
- Reset default: `0x0`
- Reset mask: `0xffffffff`

### Fields

```wavejson
{"reg": [{"name": "LEN", "bits": 32, "attr": ["wo"], "rotate": 0}], "config": {"lanes": 1, "fontsize": 10, "vspace": 80}}
```

| Bits | Type | Reset | Name |
|:------:|:------:|:-------:|:-------------------------|
| 31:0 | wo | 0x0 | [LEN](#command_len--len) |

### COMMAND_LEN . LEN
Segment Length.

For read or write segments, this field controls the
Expand Down Expand Up @@ -342,7 +362,7 @@ SPI Receive Data.
each byte the most significant bit is always pulled
from the bus first.)

- Word Aligned Offset Range: `0x24`to`0x24`
- Word Aligned Offset Range: `0x28`to`0x28`
- Size (words): `1`
- Access: `ro`
- Byte writes are *not* supported.
Expand All @@ -365,14 +385,14 @@ SPI Transmit Data.
byte-order of multi-byte data writes. (Though within
each byte the most significant bit is always sent first.)

- Word Aligned Offset Range: `0x28`to`0x28`
- Word Aligned Offset Range: `0x2c`to`0x2c`
- Size (words): `1`
- Access: `wo`
- Byte writes are supported.

## ERROR_ENABLE
Controls which classes of errors raise an interrupt.
- Offset: `0x2c`
- Offset: `0x30`
- Reset default: `0x1f`
- Reset mask: `0x1f`

Expand All @@ -396,7 +416,7 @@ Indicates that any errors that have occurred.
When an error
occurs, the corresponding bit must be cleared here before
issuing any further commands.
- Offset: `0x30`
- Offset: `0x34`
- Reset default: `0x0`
- Reset mask: `0x3f`

Expand All @@ -418,7 +438,7 @@ Indicates that any errors that have occurred.

## EVENT_ENABLE
Controls which classes of SPI events raise an interrupt.
- Offset: `0x34`
- Offset: `0x38`
- Reset default: `0x0`
- Reset mask: `0x3f`

Expand Down
2 changes: 1 addition & 1 deletion hw/ip/spi_host/dv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ All common types and methods defined at the package level can be found in
rand spi_mode_e mode;
rand spi_dir_e direction;
rand bit csaat;
rand bit [8:0] len;
rand bit [31:0] len;
} spi_host_command_t;
typedef struct packed {
Expand Down
2 changes: 1 addition & 1 deletion hw/ip/spi_host/dv/env/spi_host_env_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ package spi_host_env_pkg;
parameter uint SPI_HOST_RX_FIFO_END = (SPI_HOST_RX_FIFO_START - 1) +
spi_host_reg_pkg::SPI_HOST_RXDATA_SIZE;

parameter uint SPI_HOST_COMMAND_LEN_SIZE_BITS = 9;
parameter uint SPI_HOST_COMMAND_LEN_SIZE_BITS = 32;
// macro includes
`include "uvm_macros.svh"
`include "dv_macros.svh"
Expand Down
4 changes: 2 additions & 2 deletions hw/ip/spi_host/rtl/spi_host.sv
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,15 @@ module spi_host
assign command.configopts.cpha = configopts.cpha.q;
assign command.configopts.cpol = configopts.cpol.q;

assign command.segment.len = reg2hw.command.len.q;
assign command.segment.len = reg2hw.command_len.q;
assign command.segment.csaat = reg2hw.command.csaat.q;
assign command.segment.speed = reg2hw.command.speed.q;


logic [3:0] cmd_qes;

assign cmd_qes = {
reg2hw.command.len.qe,
reg2hw.command_len.qe,
reg2hw.command.speed.qe,
reg2hw.command.direction.qe,
reg2hw.command.csaat.qe
Expand Down
12 changes: 6 additions & 6 deletions hw/ip/spi_host/rtl/spi_host_cmd_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
package spi_host_cmd_pkg;

parameter int CSW = prim_util_pkg::vbits(spi_host_reg_pkg::NumCS);
parameter int CmdSize = CSW + 45;
parameter int CmdSize = CSW + 68;

// For decoding the direction register
typedef enum logic [1:0] {
Expand Down Expand Up @@ -37,11 +37,11 @@ package spi_host_cmd_pkg;
} configopts_t;

typedef struct packed {
logic [1:0] speed;
logic cmd_wr_en;
logic cmd_rd_en;
logic [8:0] len;
logic csaat;
logic [1:0] speed;
logic cmd_wr_en;
logic cmd_rd_en;
logic [31:0] len;
logic csaat;
} segment_t;

typedef struct packed {
Expand Down
20 changes: 10 additions & 10 deletions hw/ip/spi_host/rtl/spi_host_fsm.sv
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ module spi_host_fsm
logic [1:0] cmd_speed_d, cmd_speed_q;
logic cmd_wr_en_d, cmd_wr_en_q;
logic cmd_rd_en_d, cmd_rd_en_q;
logic [8:0] cmd_len_d, cmd_len_q;
logic [31:0] cmd_len_d, cmd_len_q;
logic csaat;
logic csaat_q;

logic [2:0] bit_cntr_d, bit_cntr_q;
logic [8:0] byte_cntr_cpha0_d, byte_cntr_cpha1_d, byte_cntr_cpha0_q, byte_cntr_cpha1_q;
logic [8:0] byte_cntr_early, byte_cntr_late;
logic [31:0] byte_cntr_cpha0_d, byte_cntr_cpha1_d, byte_cntr_cpha0_q, byte_cntr_cpha1_q;
logic [31:0] byte_cntr_early, byte_cntr_late;
logic [3:0] wait_cntr_d, wait_cntr_q;
logic last_bit, last_byte;

Expand Down Expand Up @@ -157,7 +157,7 @@ module spi_host_fsm
cmd_rd_en_q <= 1'b0;
cmd_wr_en_q <= 1'b0;
cmd_speed_q <= 2'b00;
cmd_len_q <= 9'h0;
cmd_len_q <= 32'h0;
end else begin
csid_q <= (new_command && !stall) ? csid : csid_q;
cpol_q <= (new_command && !stall) ? cpol : cpol_q;
Expand Down Expand Up @@ -442,21 +442,21 @@ module spi_host_fsm
//
always_comb begin
if (cpha_q) begin
last_byte = (byte_cntr_cpha1_q == 9'h0);
last_byte = (byte_cntr_cpha1_q == 32'h0);
end else begin
last_byte = (byte_cntr_cpha0_q == 9'h0);
last_byte = (byte_cntr_cpha0_q == 32'h0);
end
end

// Note: when updating the byte_cntr in CPHA=0 mode with a new command value, the length must
// be pulled in directly from the command bus, cmd_len_d;
assign byte_cntr_cpha0_d = sw_rst_i ? 9'h0 :
assign byte_cntr_cpha0_d = sw_rst_i ? 32'h0 :
!fsm_en ? byte_cntr_cpha0_q :
new_command ? cmd_len_d :
byte_ending_cpha0 ? byte_cntr_cpha0_q - 1 :
byte_cntr_cpha0_q;

assign byte_cntr_cpha1_d = sw_rst_i ? 9'h0 :
assign byte_cntr_cpha1_d = sw_rst_i ? 32'h0 :
!fsm_en ? byte_cntr_cpha1_q :
new_command ? cmd_len_d :
byte_ending_cpha1 ? byte_cntr_cpha1_q - 1 :
Expand Down Expand Up @@ -497,8 +497,8 @@ module spi_host_fsm
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
bit_cntr_q <= 3'h0;
byte_cntr_cpha0_q <= 9'h0;
byte_cntr_cpha1_q <= 9'h0;
byte_cntr_cpha0_q <= 32'h0;
byte_cntr_cpha1_q <= 32'h0;
wait_cntr_q <= 4'h0;
end else begin
bit_cntr_q <= stall ? bit_cntr_q : bit_cntr_d;
Expand Down
50 changes: 28 additions & 22 deletions hw/ip/spi_host/rtl/spi_host_reg_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,13 @@ package spi_host_reg_pkg;
logic q;
logic qe;
} csaat;
struct packed {
logic [8:0] q;
logic qe;
} len;
} spi_host_reg2hw_command_reg_t;

typedef struct packed {
logic [31:0] q;
logic qe;
} spi_host_reg2hw_command_len_reg_t;

typedef struct packed {
struct packed {
logic q;
Expand Down Expand Up @@ -279,14 +280,15 @@ package spi_host_reg_pkg;

// Register -> HW type
typedef struct packed {
spi_host_reg2hw_intr_state_reg_t intr_state; // [126:125]
spi_host_reg2hw_intr_enable_reg_t intr_enable; // [124:123]
spi_host_reg2hw_intr_test_reg_t intr_test; // [122:119]
spi_host_reg2hw_alert_test_reg_t alert_test; // [118:117]
spi_host_reg2hw_control_reg_t control; // [116:98]
spi_host_reg2hw_configopts_mreg_t [0:0] configopts; // [97:67]
spi_host_reg2hw_csid_reg_t csid; // [66:35]
spi_host_reg2hw_command_reg_t command; // [34:17]
spi_host_reg2hw_intr_state_reg_t intr_state; // [149:148]
spi_host_reg2hw_intr_enable_reg_t intr_enable; // [147:146]
spi_host_reg2hw_intr_test_reg_t intr_test; // [145:142]
spi_host_reg2hw_alert_test_reg_t alert_test; // [141:140]
spi_host_reg2hw_control_reg_t control; // [139:121]
spi_host_reg2hw_configopts_mreg_t [0:0] configopts; // [120:90]
spi_host_reg2hw_csid_reg_t csid; // [89:58]
spi_host_reg2hw_command_reg_t command; // [57:50]
spi_host_reg2hw_command_len_reg_t command_len; // [49:17]
spi_host_reg2hw_error_enable_reg_t error_enable; // [16:12]
spi_host_reg2hw_error_status_reg_t error_status; // [11:6]
spi_host_reg2hw_event_enable_reg_t event_enable; // [5:0]
Expand All @@ -309,9 +311,10 @@ package spi_host_reg_pkg;
parameter logic [BlockAw-1:0] SPI_HOST_CONFIGOPTS_OFFSET = 6'h 18;
parameter logic [BlockAw-1:0] SPI_HOST_CSID_OFFSET = 6'h 1c;
parameter logic [BlockAw-1:0] SPI_HOST_COMMAND_OFFSET = 6'h 20;
parameter logic [BlockAw-1:0] SPI_HOST_ERROR_ENABLE_OFFSET = 6'h 2c;
parameter logic [BlockAw-1:0] SPI_HOST_ERROR_STATUS_OFFSET = 6'h 30;
parameter logic [BlockAw-1:0] SPI_HOST_EVENT_ENABLE_OFFSET = 6'h 34;
parameter logic [BlockAw-1:0] SPI_HOST_COMMAND_LEN_OFFSET = 6'h 24;
parameter logic [BlockAw-1:0] SPI_HOST_ERROR_ENABLE_OFFSET = 6'h 30;
parameter logic [BlockAw-1:0] SPI_HOST_ERROR_STATUS_OFFSET = 6'h 34;
parameter logic [BlockAw-1:0] SPI_HOST_EVENT_ENABLE_OFFSET = 6'h 38;

// Reset values for hwext registers and their fields
parameter logic [1:0] SPI_HOST_INTR_TEST_RESVAL = 2'h 0;
Expand All @@ -320,16 +323,17 @@ package spi_host_reg_pkg;
parameter logic [0:0] SPI_HOST_ALERT_TEST_RESVAL = 1'h 0;
parameter logic [0:0] SPI_HOST_ALERT_TEST_FATAL_FAULT_RESVAL = 1'h 0;
parameter logic [13:0] SPI_HOST_COMMAND_RESVAL = 14'h 0;
parameter logic [8:0] SPI_HOST_COMMAND_LEN_RESVAL = 9'h 0;
parameter logic [0:0] SPI_HOST_COMMAND_CSAAT_RESVAL = 1'h 0;
parameter logic [1:0] SPI_HOST_COMMAND_SPEED_RESVAL = 2'h 0;
parameter logic [1:0] SPI_HOST_COMMAND_DIRECTION_RESVAL = 2'h 0;
parameter logic [31:0] SPI_HOST_COMMAND_LEN_RESVAL = 32'h 0;
parameter logic [31:0] SPI_HOST_COMMAND_LEN_LEN_RESVAL = 32'h 0;

// Window parameters
parameter logic [BlockAw-1:0] SPI_HOST_RXDATA_OFFSET = 6'h 24;
parameter logic [BlockAw-1:0] SPI_HOST_RXDATA_OFFSET = 6'h 28;
parameter int unsigned SPI_HOST_RXDATA_SIZE = 'h 4;
parameter int unsigned SPI_HOST_RXDATA_IDX = 0;
parameter logic [BlockAw-1:0] SPI_HOST_TXDATA_OFFSET = 6'h 28;
parameter logic [BlockAw-1:0] SPI_HOST_TXDATA_OFFSET = 6'h 2c;
parameter int unsigned SPI_HOST_TXDATA_SIZE = 'h 4;
parameter int unsigned SPI_HOST_TXDATA_IDX = 1;

Expand All @@ -344,13 +348,14 @@ package spi_host_reg_pkg;
SPI_HOST_CONFIGOPTS,
SPI_HOST_CSID,
SPI_HOST_COMMAND,
SPI_HOST_COMMAND_LEN,
SPI_HOST_ERROR_ENABLE,
SPI_HOST_ERROR_STATUS,
SPI_HOST_EVENT_ENABLE
} spi_host_id_e;

// Register width information to check illegal writes
parameter logic [3:0] SPI_HOST_PERMIT [12] = '{
parameter logic [3:0] SPI_HOST_PERMIT [13] = '{
4'b 0001, // index[ 0] SPI_HOST_INTR_STATE
4'b 0001, // index[ 1] SPI_HOST_INTR_ENABLE
4'b 0001, // index[ 2] SPI_HOST_INTR_TEST
Expand All @@ -360,9 +365,10 @@ package spi_host_reg_pkg;
4'b 1111, // index[ 6] SPI_HOST_CONFIGOPTS
4'b 1111, // index[ 7] SPI_HOST_CSID
4'b 0011, // index[ 8] SPI_HOST_COMMAND
4'b 0001, // index[ 9] SPI_HOST_ERROR_ENABLE
4'b 0001, // index[10] SPI_HOST_ERROR_STATUS
4'b 0001 // index[11] SPI_HOST_EVENT_ENABLE
4'b 1111, // index[ 9] SPI_HOST_COMMAND_LEN
4'b 0001, // index[10] SPI_HOST_ERROR_ENABLE
4'b 0001, // index[11] SPI_HOST_ERROR_STATUS
4'b 0001 // index[12] SPI_HOST_EVENT_ENABLE
};

endpackage
Loading

0 comments on commit 2d52326

Please sign in to comment.