mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
- Introduced a new testbench for the SDIO wrapper () to validate APB interactions. - Updated to include IRQ handling for SDIO0 and SDIO1. - Enhanced to account for performance management of SDIO0 and SDIO1. - Modified memory map tests to include SDIO base addresses and performance wait offsets. - Updated pin mapping tests to include SDIO1 signals. - Created a comprehensive suite of SDIO tests () covering various functionalities including CRC, command engine, DMA, and standalone operations. - Added a new test for verifying SDIO register parity against the RTL definition. - Adjusted SOC topology tests to reflect the addition of SDIO in the IRQ and APB fabric.
1158 lines
38 KiB
Systemverilog
1158 lines
38 KiB
Systemverilog
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
|
// retroSoC is licensed under Mulan PSL v2.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
// You may obtain a copy of the Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
|
|
`timescale 1ns / 1ps
|
|
|
|
// Verification-only native SD/SDIO card model. The model intentionally
|
|
// implements only public command/data behavior exercised by this repository;
|
|
// it is not a normative-specification replacement.
|
|
module sdio_native_card_model #(
|
|
parameter bit SdioOnly = 1'b0,
|
|
parameter bit HighCapacity = 1'b1,
|
|
parameter int BlockBytes = 512,
|
|
parameter int BlockCount = 128,
|
|
parameter int FunctionCount = 1,
|
|
parameter int FunctionBytes = 4096,
|
|
parameter int BusyCycles = 2
|
|
) (
|
|
input logic rst_n_i,
|
|
input logic sck_i,
|
|
input logic cmd_oe_i,
|
|
input logic cmd_do_i,
|
|
output logic cmd_do_o,
|
|
input logic [3:0] dat_oe_i,
|
|
input logic [3:0] dat_do_i,
|
|
output logic [3:0] dat_do_o,
|
|
output logic irq_o
|
|
);
|
|
localparam int MemoryBytes = BlockBytes * BlockCount;
|
|
localparam int FunctionStorageBytes = FunctionCount * FunctionBytes;
|
|
|
|
typedef enum logic [3:0] {
|
|
DataIdle,
|
|
DataDelay,
|
|
DataToken,
|
|
DataPayload,
|
|
DataCrc,
|
|
WriteWait,
|
|
WritePayload,
|
|
WriteCrc,
|
|
WriteResponse,
|
|
WriteBusy
|
|
} data_state_e;
|
|
|
|
logic [ 7:0] s_memory [ 0:MemoryBytes-1];
|
|
logic [ 7:0] s_function_memory [0:FunctionStorageBytes-1];
|
|
|
|
logic [ 47:0] s_cmd_shift;
|
|
integer s_cmd_bits;
|
|
logic s_cmd_response_active;
|
|
logic [135:0] s_cmd_response;
|
|
integer s_cmd_response_length;
|
|
integer s_cmd_response_index;
|
|
integer s_cmd_response_delay;
|
|
integer s_response_delay;
|
|
integer s_data_delay_cfg;
|
|
logic s_inject_crc_error;
|
|
logic s_inject_timeout;
|
|
logic s_inject_write_error;
|
|
logic s_next_cmd_crc_error;
|
|
|
|
logic [ 5:0] s_last_cmd;
|
|
logic [ 31:0] s_last_arg;
|
|
logic s_last_cmd_write;
|
|
logic s_last_cmd_data;
|
|
logic s_pending_busy;
|
|
integer s_busy_count;
|
|
|
|
data_state_e s_data_state;
|
|
logic s_data_width4;
|
|
logic s_data_fixed;
|
|
integer s_data_address;
|
|
integer s_data_bytes;
|
|
integer s_data_byte_index;
|
|
integer s_data_bit_index;
|
|
integer s_data_nibble_index;
|
|
integer s_data_crc_index;
|
|
integer s_data_delay;
|
|
logic [ 15:0] s_read_crc;
|
|
logic [ 15:0] s_read_crc_lane [ 0:3];
|
|
logic [ 15:0] s_write_crc;
|
|
logic [ 15:0] s_write_crc_lane [ 0:3];
|
|
logic [ 15:0] s_received_crc;
|
|
logic [ 15:0] s_received_crc_lane [ 0:3];
|
|
logic [ 7:0] s_write_byte;
|
|
logic [ 7:0] s_write_byte_lane [ 0:3];
|
|
integer s_write_bit_count;
|
|
integer s_write_nibble_count;
|
|
logic [ 4:0] s_write_response_token;
|
|
integer s_write_response_index;
|
|
logic s_write_response_started;
|
|
logic s_data_crc_error;
|
|
logic s_data_timeout;
|
|
logic s_irq_pending;
|
|
logic [ 2:0] s_cmd_function;
|
|
logic [ 31:0] s_cmd_address;
|
|
logic s_cmd_fixed_address;
|
|
integer s_cmd_count;
|
|
|
|
function automatic logic [6:0] crc7_40(input logic [39:0] data_i);
|
|
logic [6:0] crc;
|
|
logic feedback;
|
|
begin
|
|
crc = 7'd0;
|
|
for (integer index = 39; index >= 0; index--) begin
|
|
feedback = data_i[index] ^ crc[6];
|
|
crc = {crc[5:0], 1'b0};
|
|
if (feedback) begin
|
|
crc = crc ^ 7'h09;
|
|
end
|
|
end
|
|
return crc;
|
|
end
|
|
endfunction
|
|
|
|
function automatic logic [6:0] crc7_next(input logic [6:0] crc_i, input logic data_i);
|
|
logic feedback;
|
|
begin
|
|
feedback = data_i ^ crc_i[6];
|
|
crc7_next = {crc_i[5:0], 1'b0};
|
|
if (feedback) begin
|
|
crc7_next = crc7_next ^ 7'h09;
|
|
end
|
|
end
|
|
endfunction
|
|
|
|
function automatic logic [15:0] crc16_next(input logic [15:0] crc_i, input logic data_i);
|
|
logic feedback;
|
|
begin
|
|
feedback = data_i ^ crc_i[15];
|
|
crc16_next = {crc_i[14:0], 1'b0};
|
|
if (feedback) begin
|
|
crc16_next = crc16_next ^ 16'h1021;
|
|
end
|
|
end
|
|
endfunction
|
|
|
|
function automatic integer card_address(input logic [31:0] argument_i);
|
|
integer address;
|
|
begin
|
|
if (HighCapacity) begin
|
|
address = argument_i * BlockBytes;
|
|
end else begin
|
|
address = argument_i;
|
|
end
|
|
if (address < 0) begin
|
|
address = 0;
|
|
end
|
|
if (address >= MemoryBytes) begin
|
|
address = MemoryBytes - 1;
|
|
end
|
|
return address;
|
|
end
|
|
endfunction
|
|
|
|
function automatic integer function_address(input logic [2:0] function_i,
|
|
input logic [31:0] address_i);
|
|
integer address;
|
|
begin
|
|
address = (function_i * FunctionBytes) + address_i;
|
|
if (address < 0) begin
|
|
address = 0;
|
|
end
|
|
if (address >= FunctionStorageBytes) begin
|
|
address = FunctionStorageBytes - 1;
|
|
end
|
|
return address;
|
|
end
|
|
endfunction
|
|
|
|
function automatic logic command_frame_valid(input logic [47:0] frame_i);
|
|
begin
|
|
command_frame_valid = (frame_i[47:46] == 2'b01) &&
|
|
(frame_i[0] == 1'b1) &&
|
|
(frame_i[7:1] == crc7_40(frame_i[47:8]));
|
|
end
|
|
endfunction
|
|
|
|
function automatic logic [7:0] backing_read(input integer address_i);
|
|
integer address;
|
|
begin
|
|
address = address_i;
|
|
if (address < 0) begin
|
|
address = 0;
|
|
end
|
|
if (SdioOnly) begin
|
|
if (address >= FunctionStorageBytes) begin
|
|
address = FunctionStorageBytes - 1;
|
|
end
|
|
backing_read = s_function_memory[address];
|
|
end else begin
|
|
if (address >= MemoryBytes) begin
|
|
address = MemoryBytes - 1;
|
|
end
|
|
backing_read = s_memory[address];
|
|
end
|
|
end
|
|
endfunction
|
|
|
|
task automatic backing_write(input integer address_i, input logic [7:0] data_i);
|
|
integer address;
|
|
begin
|
|
address = address_i;
|
|
if (address < 0) begin
|
|
address = 0;
|
|
end
|
|
if (SdioOnly) begin
|
|
if (address < FunctionStorageBytes) begin
|
|
s_function_memory[address] = data_i;
|
|
end
|
|
end else if (address < MemoryBytes) begin
|
|
s_memory[address] = data_i;
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic set_response_delay(input integer cycles_i);
|
|
begin
|
|
s_response_delay = (cycles_i < 0) ? 0 : cycles_i;
|
|
end
|
|
endtask
|
|
|
|
task automatic set_data_delay(input integer cycles_i);
|
|
begin
|
|
s_data_delay_cfg = (cycles_i < 0) ? 0 : cycles_i;
|
|
end
|
|
endtask
|
|
|
|
task automatic inject_next_crc_error;
|
|
begin
|
|
s_inject_crc_error = 1'b1;
|
|
end
|
|
endtask
|
|
|
|
task automatic inject_next_timeout;
|
|
begin
|
|
s_inject_timeout = 1'b1;
|
|
end
|
|
endtask
|
|
|
|
task automatic inject_next_write_error;
|
|
begin
|
|
s_inject_write_error = 1'b1;
|
|
end
|
|
endtask
|
|
|
|
task automatic fill_memory(input logic [7:0] seed_i);
|
|
begin
|
|
if (SdioOnly) begin
|
|
for (integer index = 0; index < FunctionStorageBytes; index++) begin
|
|
s_function_memory[index] = seed_i + index[7:0];
|
|
end
|
|
end else begin
|
|
for (integer index = 0; index < MemoryBytes; index++) begin
|
|
s_memory[index] = seed_i + index[7:0];
|
|
end
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic write_backing(input integer address_i, input logic [7:0] data_i);
|
|
begin
|
|
backing_write(address_i, data_i);
|
|
end
|
|
endtask
|
|
|
|
function automatic logic [7:0] read_backing(input integer address_i);
|
|
begin
|
|
read_backing = backing_read(address_i);
|
|
end
|
|
endfunction
|
|
|
|
task automatic set_irq;
|
|
begin
|
|
s_irq_pending = 1'b1;
|
|
dat_do_o[1] = 1'b0;
|
|
irq_o = 1'b1;
|
|
end
|
|
endtask
|
|
|
|
task automatic clear_irq;
|
|
begin
|
|
s_irq_pending = 1'b0;
|
|
dat_do_o[1] = 1'b1;
|
|
irq_o = 1'b0;
|
|
end
|
|
endtask
|
|
|
|
task automatic arm_read(input integer address_i, input integer bytes_i, input logic width4_i,
|
|
input logic fixed_address_i);
|
|
begin
|
|
s_data_address = address_i;
|
|
s_data_bytes = (bytes_i < 1) ? 1 : bytes_i;
|
|
s_data_width4 = width4_i;
|
|
s_data_fixed = fixed_address_i;
|
|
s_data_byte_index = 0;
|
|
s_data_bit_index = 0;
|
|
s_data_nibble_index = 0;
|
|
s_data_crc_index = 0;
|
|
s_read_crc = 16'd0;
|
|
s_received_crc = 16'd0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_read_crc_lane[lane] = 16'd0;
|
|
s_received_crc_lane[lane] = 16'd0;
|
|
end
|
|
s_data_delay = s_data_delay_cfg;
|
|
if (s_inject_timeout) begin
|
|
s_data_state = DataIdle;
|
|
s_data_timeout = 1'b1;
|
|
s_inject_timeout = 1'b0;
|
|
end else begin
|
|
s_data_state = DataDelay;
|
|
end
|
|
dat_do_o = 4'b1111;
|
|
end
|
|
endtask
|
|
|
|
task automatic arm_write(input integer address_i, input integer bytes_i, input logic width4_i,
|
|
input logic fixed_address_i);
|
|
begin
|
|
s_data_address = address_i;
|
|
s_data_bytes = (bytes_i < 1) ? 1 : bytes_i;
|
|
s_data_width4 = width4_i;
|
|
s_data_fixed = fixed_address_i;
|
|
s_data_byte_index = 0;
|
|
s_data_bit_index = 0;
|
|
s_data_nibble_index = 0;
|
|
s_data_crc_index = 0;
|
|
s_write_byte = 8'd0;
|
|
s_write_bit_count = 0;
|
|
s_write_nibble_count = 0;
|
|
s_write_response_started = 1'b0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_write_byte_lane[lane] = 8'd0;
|
|
end
|
|
s_write_crc = 16'd0;
|
|
s_received_crc = 16'd0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_write_crc_lane[lane] = 16'd0;
|
|
s_received_crc_lane[lane] = 16'd0;
|
|
end
|
|
s_data_crc_error = 1'b0;
|
|
s_data_timeout = 1'b0;
|
|
s_data_state = WriteWait;
|
|
dat_do_o = 4'b1111;
|
|
end
|
|
endtask
|
|
|
|
task automatic make_r1_response(input logic [5:0] index_i, input logic [31:0] payload_i,
|
|
input logic crc_error_i);
|
|
logic [39:0] body;
|
|
logic [ 6:0] crc;
|
|
begin
|
|
body = {1'b0, 1'b1, index_i, payload_i};
|
|
crc = crc7_40(body);
|
|
if (crc_error_i) begin
|
|
crc[0] = ~crc[0];
|
|
end
|
|
s_cmd_response = '0;
|
|
s_cmd_response[47:0] = {body, crc, 1'b1};
|
|
s_cmd_response_length = 48;
|
|
end
|
|
endtask
|
|
|
|
task automatic make_r2_response(input logic [127:0] payload_i);
|
|
logic [127:0] body;
|
|
logic [ 6:0] crc;
|
|
begin
|
|
body = payload_i;
|
|
crc = 7'd0;
|
|
for (integer index = 127; index >= 0; index--) begin
|
|
crc = crc7_next(crc, body[index]);
|
|
end
|
|
s_cmd_response = '0;
|
|
s_cmd_response[135:0] = {body, crc, 1'b1};
|
|
s_cmd_response_length = 136;
|
|
end
|
|
endtask
|
|
|
|
task automatic queue_response(input logic [5:0] index_i, input logic [31:0] payload_i,
|
|
input logic r2_i, input logic crc_error_i);
|
|
begin
|
|
if (r2_i) begin
|
|
make_r2_response({96'd0, payload_i});
|
|
end else begin
|
|
make_r1_response(index_i, payload_i, crc_error_i);
|
|
end
|
|
s_cmd_response_active = 1'b1;
|
|
s_cmd_response_index = 0;
|
|
s_cmd_response_delay = s_response_delay;
|
|
s_cmd_response[0] = 1'b1;
|
|
if (s_cmd_response_delay == 0) begin
|
|
cmd_do_o = s_cmd_response[s_cmd_response_length-1];
|
|
end else begin
|
|
cmd_do_o = 1'b1;
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic command_received(input logic [47:0] frame_i);
|
|
logic [ 5:0] index;
|
|
logic [31:0] argument;
|
|
logic [ 6:0] expected_crc;
|
|
logic [39:0] body;
|
|
logic [31:0] response_payload;
|
|
integer address;
|
|
begin
|
|
index = frame_i[45:40];
|
|
argument = frame_i[39:8];
|
|
body = frame_i[47:8];
|
|
expected_crc = crc7_40(body);
|
|
s_last_cmd = index;
|
|
s_last_arg = argument;
|
|
s_last_cmd_data = 1'b0;
|
|
s_last_cmd_write = 1'b0;
|
|
s_cmd_function = argument[30:28];
|
|
s_cmd_address = {15'd0, argument[25:9]};
|
|
s_cmd_fixed_address = !argument[26];
|
|
s_cmd_count = {23'd0, argument[8:0]};
|
|
if (s_cmd_count == 0) begin
|
|
s_cmd_count = argument[27] ? 512 : 1;
|
|
end
|
|
if (s_inject_timeout || (frame_i[7:1] != expected_crc) || (frame_i[0] != 1'b1)) begin
|
|
s_inject_timeout = 1'b0;
|
|
s_cmd_response_active = 1'b0;
|
|
cmd_do_o = 1'b1;
|
|
end else if (SdioOnly) begin
|
|
response_payload = 32'd0;
|
|
case (index)
|
|
6'd5: begin
|
|
response_payload = 32'h80FF_8000;
|
|
queue_response(index, response_payload, 1'b0, 1'b0);
|
|
end
|
|
6'd52: begin
|
|
if (argument[31]) begin
|
|
backing_write(function_address(argument[30:28], {15'd0, argument[25:9]}),
|
|
argument[7:0]);
|
|
response_payload[7:0] = argument[7:0];
|
|
end else begin
|
|
response_payload[7:0] =
|
|
backing_read(function_address(argument[30:28], {15'd0, argument[25:9]}));
|
|
end
|
|
response_payload[31:28] = {1'b0, argument[30:28]};
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
6'd53: begin
|
|
s_last_cmd_data = 1'b1;
|
|
s_last_cmd_write = argument[31];
|
|
response_payload[7:0] = 8'h00;
|
|
response_payload[31:28] = {1'b0, argument[30:28]};
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
default: begin
|
|
queue_response(index, 32'h0000_0100, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
endcase
|
|
end else begin
|
|
response_payload = 32'h0000_0100;
|
|
case (index)
|
|
6'd0: begin
|
|
s_cmd_response_active = 1'b0;
|
|
cmd_do_o = 1'b1;
|
|
end
|
|
6'd2, 6'd9: begin
|
|
make_r2_response(index == 6'd9 ? {HighCapacity ? 1'b0 : 1'b0, 127'd0} : 128'd0);
|
|
s_cmd_response_active = 1'b1;
|
|
s_cmd_response_index = 0;
|
|
s_cmd_response_delay = s_response_delay;
|
|
cmd_do_o = (s_cmd_response_delay == 0) ? s_cmd_response[135] : 1'b1;
|
|
end
|
|
6'd8: begin
|
|
queue_response(index, argument, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
6'd3: begin
|
|
queue_response(index, 32'h0001_0000, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
6'd7, 6'd12: begin
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
s_pending_busy = 1'b1;
|
|
s_busy_count = BusyCycles;
|
|
end
|
|
6'd16, 6'd55, 6'd41, 6'd6, 6'd13: begin
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
6'd17, 6'd18: begin
|
|
s_last_cmd_data = 1'b1;
|
|
s_last_cmd_write = 1'b0;
|
|
address = card_address(argument);
|
|
s_cmd_address = address;
|
|
s_cmd_count = (index == 6'd18) ? BlockBytes : BlockBytes;
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
6'd24, 6'd25: begin
|
|
s_last_cmd_data = 1'b1;
|
|
s_last_cmd_write = 1'b1;
|
|
address = card_address(argument);
|
|
s_cmd_address = address;
|
|
s_cmd_count = BlockBytes;
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
default: begin
|
|
queue_response(index, response_payload, 1'b0, s_inject_crc_error);
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic decode_command_frame(input logic [47:0] raw_i);
|
|
begin
|
|
if (command_frame_valid(raw_i)) begin
|
|
command_received(raw_i);
|
|
end else begin
|
|
$fatal(1, "SDIO card observed an invalid command frame: %h", raw_i);
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic drive_read_data;
|
|
integer address;
|
|
logic [7:0] byte_value;
|
|
begin
|
|
if (s_data_width4) begin
|
|
dat_do_o = 4'b1111;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (lane < (s_data_bytes - s_data_byte_index)) begin
|
|
address = s_data_address + (s_data_fixed ? 0 : s_data_byte_index + lane);
|
|
byte_value = backing_read(address);
|
|
dat_do_o[lane] = byte_value[7-(s_data_bit_index%8)];
|
|
end
|
|
end
|
|
end else begin
|
|
address = s_data_address + (s_data_fixed ? 0 : s_data_byte_index);
|
|
byte_value = backing_read(address);
|
|
dat_do_o = 4'b1111;
|
|
dat_do_o[0] = byte_value[7-(s_data_bit_index%8)];
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic advance_read_crc;
|
|
integer address;
|
|
logic [7:0] byte_value;
|
|
begin
|
|
if (s_data_width4) begin
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (lane < (s_data_bytes - s_data_byte_index)) begin
|
|
address = s_data_address + (s_data_fixed ? 0 : s_data_byte_index + lane);
|
|
byte_value = backing_read(address);
|
|
s_read_crc_lane[lane] =
|
|
crc16_next(s_read_crc_lane[lane], byte_value[7-(s_data_bit_index%8)]);
|
|
end
|
|
end
|
|
s_data_bit_index = s_data_bit_index + 1;
|
|
if (s_data_bit_index >= 8) begin
|
|
s_data_byte_index = s_data_byte_index + 4;
|
|
if (s_data_byte_index > s_data_bytes) begin
|
|
s_data_byte_index = s_data_bytes;
|
|
end
|
|
if (s_data_byte_index < s_data_bytes) begin
|
|
s_data_bit_index = 0;
|
|
end
|
|
end
|
|
end else begin
|
|
address = s_data_address + (s_data_fixed ? 0 : s_data_byte_index);
|
|
byte_value = backing_read(address);
|
|
s_read_crc = crc16_next(s_read_crc, byte_value[7-(s_data_bit_index%8)]);
|
|
if ((s_data_bit_index % 8) == 7) begin
|
|
s_data_byte_index = s_data_byte_index + 1;
|
|
end
|
|
s_data_bit_index = s_data_bit_index + 1;
|
|
end
|
|
end
|
|
endtask
|
|
|
|
task automatic sample_write_payload(input logic [3:0] bits_i);
|
|
begin
|
|
if (s_data_width4) begin
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (lane < (s_data_bytes - s_data_byte_index)) begin
|
|
s_write_byte_lane[lane] = {s_write_byte_lane[lane][6:0], bits_i[lane]};
|
|
s_write_crc_lane[lane] = crc16_next(s_write_crc_lane[lane], bits_i[lane]);
|
|
end
|
|
end
|
|
s_write_bit_count = s_write_bit_count + 1;
|
|
if (s_write_bit_count == 8) begin
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (lane < (s_data_bytes - s_data_byte_index)) begin
|
|
backing_write(s_data_address + (s_data_fixed ? 0 : s_data_byte_index + lane),
|
|
s_write_byte_lane[lane]);
|
|
end
|
|
end
|
|
s_data_byte_index = s_data_byte_index + 4;
|
|
if (s_data_byte_index > s_data_bytes) begin
|
|
s_data_byte_index = s_data_bytes;
|
|
end
|
|
s_write_bit_count = 0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_write_byte_lane[lane] = 8'd0;
|
|
end
|
|
end
|
|
end else begin
|
|
s_write_byte = {s_write_byte[6:0], bits_i[0]};
|
|
s_write_bit_count = s_write_bit_count + 1;
|
|
s_write_crc = crc16_next(s_write_crc, bits_i[0]);
|
|
if (s_write_bit_count == 8) begin
|
|
backing_write(s_data_address + (s_data_fixed ? 0 : s_data_byte_index), s_write_byte);
|
|
s_data_byte_index = s_data_byte_index + 1;
|
|
s_write_bit_count = 0;
|
|
end
|
|
end
|
|
if (s_data_byte_index >= s_data_bytes) begin
|
|
s_data_state = WriteCrc;
|
|
s_data_crc_index = 0;
|
|
s_received_crc = 16'd0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_received_crc_lane[lane] = 16'd0;
|
|
end
|
|
end
|
|
end
|
|
endtask
|
|
|
|
always @(posedge sck_i or negedge rst_n_i) begin
|
|
if (!rst_n_i) begin
|
|
s_cmd_shift = '0;
|
|
s_cmd_bits = 0;
|
|
s_cmd_response_active = 1'b0;
|
|
s_cmd_response = '0;
|
|
s_cmd_response_length = 48;
|
|
s_cmd_response_index = 0;
|
|
s_cmd_response_delay = 0;
|
|
s_response_delay = 0;
|
|
s_inject_crc_error = 1'b0;
|
|
s_inject_timeout = 1'b0;
|
|
s_inject_write_error = 1'b0;
|
|
s_next_cmd_crc_error = 1'b0;
|
|
s_last_cmd = 6'd0;
|
|
s_last_arg = 32'd0;
|
|
s_last_cmd_write = 1'b0;
|
|
s_last_cmd_data = 1'b0;
|
|
s_pending_busy = 1'b0;
|
|
s_busy_count = 0;
|
|
end else if (cmd_oe_i) begin
|
|
if (s_cmd_bits == 0) begin
|
|
s_cmd_shift = {{47{1'b0}}, cmd_do_i};
|
|
s_cmd_bits = 1;
|
|
end else if (s_cmd_bits != 0) begin
|
|
s_cmd_shift = {s_cmd_shift[46:0], cmd_do_i};
|
|
if (s_cmd_bits == 47) begin
|
|
decode_command_frame(s_cmd_shift);
|
|
s_cmd_bits = 0;
|
|
end else begin
|
|
s_cmd_bits = s_cmd_bits + 1;
|
|
end
|
|
end
|
|
end else begin
|
|
s_cmd_bits = 0;
|
|
end
|
|
end
|
|
|
|
always @(negedge sck_i or negedge rst_n_i) begin
|
|
if (!rst_n_i) begin
|
|
s_cmd_response_active = 1'b0;
|
|
s_cmd_response_index = 0;
|
|
s_cmd_response_delay = 0;
|
|
end else if (s_cmd_response_active) begin
|
|
if (s_cmd_response_delay > 0) begin
|
|
s_cmd_response_delay = s_cmd_response_delay - 1;
|
|
cmd_do_o = 1'b1;
|
|
if (s_cmd_response_delay == 0) begin
|
|
cmd_do_o = s_cmd_response[s_cmd_response_length-1];
|
|
end
|
|
end else if (s_cmd_response_index + 1 >= s_cmd_response_length) begin
|
|
s_cmd_response_active = 1'b0;
|
|
cmd_do_o = 1'b1;
|
|
end else begin
|
|
cmd_do_o = s_cmd_response[s_cmd_response_length-1-(s_cmd_response_index+1)];
|
|
s_cmd_response_index = s_cmd_response_index + 1;
|
|
if (s_cmd_response_index + 1 >= s_cmd_response_length) begin
|
|
s_cmd_response_active = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge sck_i or negedge rst_n_i) begin
|
|
if (!rst_n_i) begin
|
|
s_cmd_shift = '0;
|
|
s_cmd_bits = 0;
|
|
s_cmd_response_active = 1'b0;
|
|
s_cmd_response = '0;
|
|
s_cmd_response_length = 48;
|
|
s_cmd_response_index = 0;
|
|
s_cmd_response_delay = 0;
|
|
s_response_delay = 0;
|
|
s_inject_crc_error = 1'b0;
|
|
s_inject_timeout = 1'b0;
|
|
s_inject_write_error = 1'b0;
|
|
s_next_cmd_crc_error = 1'b0;
|
|
s_last_cmd = 6'd0;
|
|
s_last_arg = 32'd0;
|
|
s_last_cmd_write = 1'b0;
|
|
s_last_cmd_data = 1'b0;
|
|
s_pending_busy = 1'b0;
|
|
s_busy_count = 0;
|
|
s_data_state = DataIdle;
|
|
s_data_width4 = 1'b0;
|
|
s_data_fixed = 1'b0;
|
|
s_data_address = 0;
|
|
s_data_bytes = 0;
|
|
s_data_byte_index = 0;
|
|
s_data_bit_index = 0;
|
|
s_data_nibble_index = 0;
|
|
s_data_crc_index = 0;
|
|
s_data_delay = 0;
|
|
s_read_crc = 16'd0;
|
|
s_write_crc = 16'd0;
|
|
s_received_crc = 16'd0;
|
|
s_write_byte = 8'd0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_write_byte_lane[lane] = 8'd0;
|
|
end
|
|
s_write_bit_count = 0;
|
|
s_write_nibble_count = 0;
|
|
s_write_response_token = 5'b00101;
|
|
s_write_response_index = 0;
|
|
s_write_response_started = 1'b0;
|
|
s_data_crc_error = 1'b0;
|
|
s_data_timeout = 1'b0;
|
|
s_irq_pending = 1'b0;
|
|
s_cmd_function = 3'd0;
|
|
s_cmd_address = 32'd0;
|
|
s_cmd_fixed_address = 1'b0;
|
|
s_cmd_count = 0;
|
|
cmd_do_o = 1'b1;
|
|
dat_do_o = 4'b1111;
|
|
irq_o = 1'b0;
|
|
end else begin
|
|
if (s_pending_busy) begin
|
|
dat_do_o[0] = 1'b0;
|
|
if (s_busy_count > 0) begin
|
|
s_busy_count = s_busy_count - 1;
|
|
end else begin
|
|
s_pending_busy = 1'b0;
|
|
dat_do_o[0] = 1'b1;
|
|
end
|
|
end
|
|
|
|
if (s_data_state == WriteBusy) begin
|
|
dat_do_o[0] = 1'b0;
|
|
if (s_busy_count > 0) begin
|
|
s_busy_count = s_busy_count - 1;
|
|
end else begin
|
|
s_data_state = DataIdle;
|
|
dat_do_o[0] = 1'b1;
|
|
end
|
|
end
|
|
|
|
case (s_data_state)
|
|
DataDelay: begin
|
|
dat_do_o = 4'b1111;
|
|
if (s_data_delay > 0) begin
|
|
s_data_delay = s_data_delay - 1;
|
|
end else begin
|
|
s_data_state = DataToken;
|
|
dat_do_o = 4'b0000;
|
|
end
|
|
end
|
|
DataToken: begin
|
|
s_data_byte_index = 0;
|
|
s_data_bit_index = 0;
|
|
s_data_nibble_index = 0;
|
|
s_data_crc_index = 0;
|
|
s_read_crc = 16'd0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_read_crc_lane[lane] = 16'd0;
|
|
end
|
|
s_data_state = DataPayload;
|
|
drive_read_data();
|
|
end
|
|
DataPayload: begin
|
|
advance_read_crc();
|
|
if (s_data_width4) begin
|
|
if (s_data_bit_index >= 8 && s_data_byte_index >= s_data_bytes) begin
|
|
s_data_state = DataCrc;
|
|
s_data_crc_index = 0;
|
|
dat_do_o = 4'b1111;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (lane < s_data_bytes) begin
|
|
dat_do_o[lane] = s_read_crc_lane[lane][15];
|
|
end
|
|
end
|
|
end else begin
|
|
drive_read_data();
|
|
end
|
|
end else if (s_data_bit_index >= (s_data_bytes * 8)) begin
|
|
s_data_state = DataCrc;
|
|
s_data_crc_index = 0;
|
|
dat_do_o = 4'b1111;
|
|
dat_do_o[0] = s_read_crc[15];
|
|
end else begin
|
|
drive_read_data();
|
|
end
|
|
end
|
|
DataCrc: begin
|
|
if (s_data_width4) begin
|
|
if (s_data_crc_index == 15) begin
|
|
s_data_state = DataIdle;
|
|
dat_do_o = 4'b1111;
|
|
end else begin
|
|
s_data_crc_index = s_data_crc_index + 1;
|
|
dat_do_o = 4'b1111;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (lane < s_data_bytes) begin
|
|
dat_do_o[lane] = s_read_crc_lane[lane][15-(s_data_crc_index+1)];
|
|
end
|
|
end
|
|
end
|
|
end else if (s_data_crc_index == 15) begin
|
|
s_data_state = DataIdle;
|
|
dat_do_o = 4'b1111;
|
|
end else begin
|
|
s_data_crc_index = s_data_crc_index + 1;
|
|
dat_do_o = 4'b1111;
|
|
dat_do_o[0] = s_read_crc[15-(s_data_crc_index+1)];
|
|
end
|
|
end
|
|
WriteResponse: begin
|
|
dat_do_o = 4'b1111;
|
|
if (!s_write_response_started) begin
|
|
s_write_response_started = 1'b1;
|
|
dat_do_o[0] = s_write_response_token[4];
|
|
end else if (s_write_response_index < 4) begin
|
|
s_write_response_index = s_write_response_index + 1;
|
|
dat_do_o[0] = s_write_response_token[4-s_write_response_index];
|
|
end else begin
|
|
s_data_state = (s_write_response_token == 5'b00101) ? WriteBusy : DataIdle;
|
|
s_busy_count = BusyCycles;
|
|
dat_do_o[0] = (s_write_response_token == 5'b00101) ? 1'b0 : 1'b1;
|
|
end
|
|
end
|
|
default: begin
|
|
end
|
|
endcase
|
|
|
|
if ((s_data_state != DataPayload) && (s_data_state != DataCrc)) begin
|
|
if (s_irq_pending && !s_pending_busy) begin
|
|
dat_do_o[1] = 1'b0;
|
|
end else if (s_data_state == DataIdle) begin
|
|
dat_do_o[1] = 1'b1;
|
|
end
|
|
end
|
|
irq_o = s_irq_pending;
|
|
end
|
|
end
|
|
|
|
always @(negedge sck_i or negedge rst_n_i) begin
|
|
if (!rst_n_i) begin
|
|
s_received_crc = 16'd0;
|
|
s_write_byte = 8'd0;
|
|
s_write_bit_count = 0;
|
|
s_write_nibble_count = 0;
|
|
end else begin
|
|
case (s_data_state)
|
|
WriteWait: begin
|
|
if ((dat_oe_i != 4'b0000) && (dat_do_i == 4'b0000)) begin
|
|
s_data_state = WritePayload;
|
|
s_write_bit_count = 0;
|
|
s_write_nibble_count = 0;
|
|
s_write_byte = 8'd0;
|
|
end else if (dat_oe_i != 4'b0000) begin
|
|
$fatal(1, "SDIO card observed a missing data token: %b", dat_do_i);
|
|
end
|
|
end
|
|
WritePayload: begin
|
|
sample_write_payload(dat_do_i);
|
|
end
|
|
WriteCrc: begin
|
|
if (s_data_width4) begin
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_received_crc_lane[lane] = {s_received_crc_lane[lane][14:0], dat_do_i[lane]};
|
|
end
|
|
end else begin
|
|
s_received_crc = {s_received_crc[14:0], dat_do_i[0]};
|
|
end
|
|
if (s_data_crc_index == 15) begin
|
|
s_data_crc_error = 1'b0;
|
|
if (s_data_width4) begin
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
if (s_received_crc_lane[lane] != s_write_crc_lane[lane]) begin
|
|
s_data_crc_error = 1'b1;
|
|
end
|
|
end
|
|
end else if (s_received_crc != s_write_crc) begin
|
|
s_data_crc_error = 1'b1;
|
|
end
|
|
if (s_inject_crc_error) begin
|
|
s_data_crc_error = 1'b1;
|
|
s_inject_crc_error = 1'b0;
|
|
end
|
|
if (s_inject_write_error) begin
|
|
s_write_response_token = 5'b01101;
|
|
s_inject_write_error = 1'b0;
|
|
end else if (s_data_crc_error) begin
|
|
s_write_response_token = 5'b01011;
|
|
end else begin
|
|
s_write_response_token = 5'b00101;
|
|
end
|
|
s_write_response_index = 0;
|
|
s_write_response_started = 1'b0;
|
|
s_data_state = WriteResponse;
|
|
dat_do_o = 4'b1111;
|
|
dat_do_o[0] = s_write_response_token[4];
|
|
end else begin
|
|
s_data_crc_index = s_data_crc_index + 1;
|
|
end
|
|
end
|
|
default: begin
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
initial begin
|
|
if ((BlockBytes < 1) || (BlockCount < 1) || (FunctionCount < 1) || (FunctionBytes < 256)) begin
|
|
$fatal(1, "sdio_native_card_model: invalid storage geometry");
|
|
end
|
|
cmd_do_o = 1'b1;
|
|
dat_do_o = 4'b1111;
|
|
irq_o = 1'b0;
|
|
s_cmd_shift = '0;
|
|
s_cmd_bits = 0;
|
|
s_cmd_response_active = 1'b0;
|
|
s_cmd_response = '0;
|
|
s_cmd_response_length = 48;
|
|
s_cmd_response_index = 0;
|
|
s_cmd_response_delay = 0;
|
|
s_last_cmd = 6'd0;
|
|
s_last_arg = 32'd0;
|
|
s_last_cmd_write = 1'b0;
|
|
s_last_cmd_data = 1'b0;
|
|
s_pending_busy = 1'b0;
|
|
s_busy_count = 0;
|
|
s_data_state = DataIdle;
|
|
s_data_width4 = 1'b0;
|
|
s_data_fixed = 1'b0;
|
|
s_data_address = 0;
|
|
s_data_bytes = 0;
|
|
s_data_byte_index = 0;
|
|
s_data_bit_index = 0;
|
|
s_data_nibble_index = 0;
|
|
s_data_crc_index = 0;
|
|
s_data_delay = 0;
|
|
s_read_crc = 16'd0;
|
|
s_write_crc = 16'd0;
|
|
s_received_crc = 16'd0;
|
|
s_write_byte = 8'd0;
|
|
for (integer lane = 0; lane < 4; lane++) begin
|
|
s_write_byte_lane[lane] = 8'd0;
|
|
end
|
|
s_write_bit_count = 0;
|
|
s_write_nibble_count = 0;
|
|
s_write_response_token = 5'b00101;
|
|
s_write_response_index = 0;
|
|
s_write_response_started = 1'b0;
|
|
s_data_crc_error = 1'b0;
|
|
s_data_timeout = 1'b0;
|
|
s_irq_pending = 1'b0;
|
|
s_cmd_function = 3'd0;
|
|
s_cmd_address = 32'd0;
|
|
s_cmd_fixed_address = 1'b0;
|
|
s_cmd_count = 0;
|
|
s_response_delay = 0;
|
|
s_data_delay_cfg = 0;
|
|
s_data_delay = 0;
|
|
s_inject_crc_error = 1'b0;
|
|
s_inject_timeout = 1'b0;
|
|
s_inject_write_error = 1'b0;
|
|
s_irq_pending = 1'b0;
|
|
for (integer index = 0; index < MemoryBytes; index++) begin
|
|
s_memory[index] = 8'd0;
|
|
end
|
|
for (integer index = 0; index < FunctionStorageBytes; index++) begin
|
|
s_function_memory[index] = 8'd0;
|
|
end
|
|
if (SdioOnly) begin
|
|
s_function_memory[0] = 8'h03;
|
|
s_function_memory[2] = 8'h01;
|
|
s_function_memory[3] = 8'h01;
|
|
s_function_memory[4] = 8'h00;
|
|
s_function_memory[5] = 8'h00;
|
|
s_function_memory[6] = 8'h00;
|
|
s_function_memory[7] = 8'h00;
|
|
s_function_memory[8] = 8'h00;
|
|
s_function_memory[9] = 8'h00;
|
|
s_function_memory[10] = 8'h02;
|
|
s_function_memory[11] = 8'h00;
|
|
s_function_memory[19] = 8'h01;
|
|
for (integer function_index = 1; function_index < FunctionCount; function_index++) begin
|
|
s_function_memory[function_index*FunctionBytes] = 8'h01;
|
|
end
|
|
end
|
|
end
|
|
endmodule
|
|
|
|
module sdio_sd_memory_model #(
|
|
parameter bit HighCapacity = 1'b1,
|
|
parameter int BlockBytes = 512,
|
|
parameter int BlockCount = 128,
|
|
parameter int BusyCycles = 2
|
|
) (
|
|
input logic rst_n_i,
|
|
input logic sck_i,
|
|
input logic cmd_oe_i,
|
|
input logic cmd_do_i,
|
|
output logic cmd_do_o,
|
|
input logic [3:0] dat_oe_i,
|
|
input logic [3:0] dat_do_i,
|
|
output logic [3:0] dat_do_o,
|
|
output logic irq_o
|
|
);
|
|
sdio_native_card_model #(
|
|
.SdioOnly (1'b0),
|
|
.HighCapacity(HighCapacity),
|
|
.BlockBytes (BlockBytes),
|
|
.BlockCount (BlockCount),
|
|
.BusyCycles (BusyCycles)
|
|
) u_model (
|
|
.rst_n_i (rst_n_i),
|
|
.sck_i (sck_i),
|
|
.cmd_oe_i(cmd_oe_i),
|
|
.cmd_do_i(cmd_do_i),
|
|
.cmd_do_o(cmd_do_o),
|
|
.dat_oe_i(dat_oe_i),
|
|
.dat_do_i(dat_do_i),
|
|
.dat_do_o(dat_do_o),
|
|
.irq_o (irq_o)
|
|
);
|
|
|
|
task automatic set_response_delay(input integer cycles_i);
|
|
u_model.set_response_delay(cycles_i);
|
|
endtask
|
|
task automatic set_data_delay(input integer cycles_i);
|
|
u_model.set_data_delay(cycles_i);
|
|
endtask
|
|
task automatic inject_next_crc_error;
|
|
u_model.inject_next_crc_error();
|
|
endtask
|
|
task automatic inject_next_timeout;
|
|
u_model.inject_next_timeout();
|
|
endtask
|
|
task automatic inject_next_write_error;
|
|
u_model.inject_next_write_error();
|
|
endtask
|
|
task automatic fill_memory(input logic [7:0] seed_i);
|
|
u_model.fill_memory(seed_i);
|
|
endtask
|
|
task automatic arm_read(input integer address_i, input integer bytes_i, input logic width4_i,
|
|
input logic fixed_address_i);
|
|
u_model.arm_read(address_i, bytes_i, width4_i, fixed_address_i);
|
|
endtask
|
|
task automatic arm_write(input integer address_i, input integer bytes_i, input logic width4_i,
|
|
input logic fixed_address_i);
|
|
u_model.arm_write(address_i, bytes_i, width4_i, fixed_address_i);
|
|
endtask
|
|
function automatic logic [7:0] read_backing(input integer address_i);
|
|
read_backing = u_model.read_backing(address_i);
|
|
endfunction
|
|
task automatic write_backing(input integer address_i, input logic [7:0] data_i);
|
|
u_model.write_backing(address_i, data_i);
|
|
endtask
|
|
endmodule
|
|
|
|
module sdio_sdio_model #(
|
|
parameter int FunctionCount = 2,
|
|
parameter int FunctionBytes = 4096,
|
|
parameter int BusyCycles = 2
|
|
) (
|
|
input logic rst_n_i,
|
|
input logic sck_i,
|
|
input logic cmd_oe_i,
|
|
input logic cmd_do_i,
|
|
output logic cmd_do_o,
|
|
input logic [3:0] dat_oe_i,
|
|
input logic [3:0] dat_do_i,
|
|
output logic [3:0] dat_do_o,
|
|
output logic irq_o
|
|
);
|
|
sdio_native_card_model #(
|
|
.SdioOnly (1'b1),
|
|
.FunctionCount(FunctionCount),
|
|
.FunctionBytes(FunctionBytes),
|
|
.BusyCycles (BusyCycles)
|
|
) u_model (
|
|
.rst_n_i (rst_n_i),
|
|
.sck_i (sck_i),
|
|
.cmd_oe_i(cmd_oe_i),
|
|
.cmd_do_i(cmd_do_i),
|
|
.cmd_do_o(cmd_do_o),
|
|
.dat_oe_i(dat_oe_i),
|
|
.dat_do_i(dat_do_i),
|
|
.dat_do_o(dat_do_o),
|
|
.irq_o (irq_o)
|
|
);
|
|
|
|
task automatic set_response_delay(input integer cycles_i);
|
|
u_model.set_response_delay(cycles_i);
|
|
endtask
|
|
task automatic set_data_delay(input integer cycles_i);
|
|
u_model.set_data_delay(cycles_i);
|
|
endtask
|
|
task automatic inject_next_crc_error;
|
|
u_model.inject_next_crc_error();
|
|
endtask
|
|
task automatic inject_next_timeout;
|
|
u_model.inject_next_timeout();
|
|
endtask
|
|
task automatic inject_next_write_error;
|
|
u_model.inject_next_write_error();
|
|
endtask
|
|
task automatic arm_read(input integer address_i, input integer bytes_i, input logic width4_i,
|
|
input logic fixed_address_i);
|
|
u_model.arm_read(address_i, bytes_i, width4_i, fixed_address_i);
|
|
endtask
|
|
task automatic arm_write(input integer address_i, input integer bytes_i, input logic width4_i,
|
|
input logic fixed_address_i);
|
|
u_model.arm_write(address_i, bytes_i, width4_i, fixed_address_i);
|
|
endtask
|
|
task automatic set_irq;
|
|
u_model.set_irq();
|
|
endtask
|
|
task automatic clear_irq;
|
|
u_model.clear_irq();
|
|
endtask
|
|
function automatic logic [7:0] read_backing(input integer address_i);
|
|
read_backing = u_model.read_backing(address_i);
|
|
endfunction
|
|
task automatic write_backing(input integer address_i, input logic [7:0] data_i);
|
|
u_model.write_backing(address_i, data_i);
|
|
endtask
|
|
endmodule
|