首次提交

This commit is contained in:
eesimple
2026-03-06 16:22:17 +08:00
commit b8fe9f77ec
465 changed files with 115939 additions and 0 deletions

322
ssp_rx/src/ssp_rx.sv Normal file
View File

@@ -0,0 +1,322 @@
//---------------------------------------------------------------------------------------
// filename: ssp_rx.sv
// description: synchronous serial peripheral interface receiver
// author: leguoqing@paisat.cn
//---------------------------------------------------------------------------------------
module ssp_rx #(
parameter integer FREQ_HZ = 100e6,
parameter integer SSP_HZ = 10e6,
parameter integer STREAM_WIDTH = 8
) (
input logic clk,
input logic aresetn,
input logic enable,
output logic [STREAM_WIDTH-1:0] tdata,
output logic tvalid,
output logic tkeep,
output logic tstrb,
output logic tlast,
input logic tready,
input logic ssp_clk,
input logic ssp_csn,
input logic ssp_data,
input logic [31:0] config_00,
input logic [31:0] config_01,
input logic [31:0] config_02,
output logic [31:0] status_00,
output logic [31:0] status_01,
output logic [31:0] status_02,
output logic [31:0] status_03,
output logic [31:0] status_04,
output logic [31:0] status_05,
output logic [31:0] status_06,
output logic [31:0] status_07
);
if ($ceil(FREQ_HZ/SSP_HZ) != $floor(FREQ_HZ/SSP_HZ)) begin
$fatal("%m: FREQ_HZ must be an integer multiple of SSP_HZ!");
end else if (FREQ_HZ/SSP_HZ < 6) begin
$fatal("%m: FREQ_HZ/SSP_HZ < 6!");
end else if ((FREQ_HZ/SSP_HZ)%2 != 0) begin
$warning("%m: FREQ_HZ/SSP_HZ is not an even number, which means the SSP clock will be asymmetric!");
end
localparam integer CLK_HI = $ceil(FREQ_HZ/(2*SSP_HZ));
localparam integer CLK_LO = $floor(FREQ_HZ/(2*SSP_HZ));
// State encoding
typedef enum logic [1:0] {
IDLE,
RECEIVE,
LAST
} state_t;
state_t state, state_next;
logic [ 7:0] fsm_hardcode;
logic [31:0] counter_rx_data;
logic [31:0] counter_rx_last;
logic [31:0] counter_half_beats;
logic [31:0] counter_clk_falling;
logic [31:0] counter_data_rising;
logic [31:0] counter_csn_falling;
logic [31:0] counter_csn_rising;
logic mode_wire3;
logic mode_wire2;
logic [3:0] header_size;
logic [15:0] frame_size;
logic [31:0] frame_header;
logic [31:0] frame_header_mask;
logic cfg_valid;
logic [31:0] frame_header_candidate;
logic header_matched;
logic [31:0] header_mask_shifted;
logic [15:0] beats_cnt;
logic [STREAM_WIDTH-1:0] rx_shift_reg;
logic [$clog2(STREAM_WIDTH)-1:0] rx_bit_cnt;
logic data_valid;
logic [STREAM_WIDTH-1:0] data;
logic [2:0] ssp_csn_sync;
logic [2:0] ssp_data_sync;
logic [2:0] ssp_clk_sync;
logic ssp_clk_falling;
assign mode_wire3 = config_00[0];
assign mode_wire2 = config_00[1];
assign header_size = config_00[8 +: 4];
assign frame_size = config_00[16 +: 16];
assign frame_header = config_01;
assign frame_header_mask = config_02;
assign cfg_valid = (mode_wire3 && !mode_wire2) || (!mode_wire3 && mode_wire2 && header_size != 0 && frame_size != 0 && frame_header_mask != 0 && frame_header != 0);
assign status_00 = {8'(cfg_valid), 5'h0, ssp_clk_sync[2], ssp_csn_sync[2], ssp_data_sync[2], fsm_hardcode, 8'(enable)};
assign status_01 = counter_rx_data;
assign status_02 = counter_rx_last;
assign status_03 = counter_half_beats;
assign status_04 = counter_clk_falling;
assign status_05 = counter_data_rising;
assign status_06 = counter_csn_falling;
assign status_07 = counter_csn_rising;
// synchronize ssp_clk/ssp_csn/ssp_data to clk domain
always_ff @(posedge clk) begin
if (!aresetn) begin
ssp_csn_sync <= 3'b111;
ssp_data_sync <= 3'b000;
ssp_clk_sync <= 3'b000;
end else begin
ssp_csn_sync <= {ssp_csn_sync[1:0], ssp_csn};
ssp_data_sync <= {ssp_data_sync[1:0], ssp_data};
ssp_clk_sync <= {ssp_clk_sync[1:0], ssp_clk};
end
end
assign ssp_clk_falling = (ssp_clk_sync[2:1] == 2'b10);
always_ff @(posedge clk) begin
if (!aresetn) begin
counter_clk_falling <= 32'h0;
counter_data_rising <= 32'h0;
counter_csn_falling <= 32'h0;
counter_csn_rising <= 32'h0;
end else begin
if (ssp_clk_falling) begin
counter_clk_falling <= counter_clk_falling + 32'h1;
end
if (ssp_data_sync[2:1] == 2'b01) begin
counter_data_rising <= counter_data_rising + 32'h1;
end
if (ssp_csn_sync[2:1] == 2'b10) begin
counter_csn_falling <= counter_csn_falling + 32'h1;
end
if (ssp_csn_sync[2:1] == 2'b01) begin
counter_csn_rising <= counter_csn_rising + 32'h1;
end
end
end
always_ff @(posedge clk) begin
if (!aresetn) begin
fsm_hardcode <= 8'h0;
end else begin
case (state)
IDLE:
fsm_hardcode <= 8'h0;
RECEIVE:
fsm_hardcode <= 8'h1;
LAST:
fsm_hardcode <= 8'h2;
default:
fsm_hardcode <= 8'hF;
endcase
end
end
// FSM: State register
always_ff @(posedge clk) begin
if (!aresetn) begin
state <= IDLE;
end else begin
state <= state_next;
end
end
// FSM: Next state logic
always_comb begin
state_next = state;
case (state)
IDLE: begin
if (cfg_valid)
if (mode_wire3) begin
if (!ssp_csn_sync[1]) begin
state_next = RECEIVE;
end
end else if (mode_wire2) begin
state_next = RECEIVE;
end
end
RECEIVE: begin
if (mode_wire3) begin
if (ssp_csn_sync[1]) begin
state_next = LAST;
end
end else if (mode_wire2) begin
if (header_matched && |frame_size != 0 && beats_cnt == frame_size) begin
state_next = LAST;
end
end
if (!cfg_valid) begin
state_next = IDLE;
end
end
LAST: begin
state_next = IDLE;
end
endcase
end
// FSM: Output logic
always_ff @(posedge clk) begin
if (!aresetn) begin
rx_shift_reg <= 0;
rx_bit_cnt <= 0;
data_valid <= 0;
data <= 0;
tvalid <= 0;
tstrb <= 0;
tkeep <= 0;
tlast <= 0;
tdata <= 0;
header_matched <= 0;
frame_header_candidate <= 0;
beats_cnt <= 0;
header_mask_shifted <= 0;
end else begin
if (tready) begin
tvalid <= 1'b0;
tstrb <= 1'b0;
tkeep <= 1'b0;
tlast <= 1'b0;
end
case (state)
IDLE: begin
rx_shift_reg <= 0;
rx_bit_cnt <= 0;
data_valid <= 0;
data <= 0;
header_matched <= 0;
frame_header_candidate <= 0;
beats_cnt <= 0;
header_mask_shifted <= 0;
end
RECEIVE: begin
if (ssp_clk_falling) begin
rx_shift_reg <= {rx_shift_reg[STREAM_WIDTH-2:0], ssp_data_sync[1]};
rx_bit_cnt <= rx_bit_cnt + 1'b1;
if (mode_wire3 == 1 || (mode_wire2 == 1 && header_matched)) begin
if (rx_bit_cnt == STREAM_WIDTH-1) begin
rx_bit_cnt <= 0;
if (mode_wire3 == 0)
beats_cnt <= beats_cnt + 1'b1;
data <= {rx_shift_reg[STREAM_WIDTH-2:0], ssp_data_sync[1]};
data_valid <= 1'b1; // defer one beat
if (data_valid) begin
tvalid <= 1'b1;
tstrb <= 1'b1;
tkeep <= 1'b1;
tlast <= 1'b0;
tdata <= data;
end
end
end
if (mode_wire3 == 0 && mode_wire2 == 1 && header_matched == 0) begin
frame_header_candidate <= {frame_header_candidate[30:0], ssp_data_sync[1]};
end
end
if (mode_wire3 == 0 && mode_wire2 == 1 && frame_header_candidate == (frame_header & frame_header_mask)) begin
header_matched <= 1'b1;
rx_bit_cnt <= 0;
header_mask_shifted <= frame_header_mask;
end
// implicitly condition: has enough cycle to accomplish before next beat arrives
if (header_matched && beats_cnt < header_size) begin
tvalid <= 1'b1;
tstrb <= 1'b1;
tkeep <= |header_mask_shifted[32-STREAM_WIDTH+:STREAM_WIDTH];
tlast <= 1'b0;
tdata <= frame_header_candidate[32-STREAM_WIDTH+:STREAM_WIDTH];
if (tvalid && tready) begin
tvalid <= 1'b0;
tstrb <= 1'b0;
tkeep <= 1'b0;
tlast <= 1'b0;
frame_header_candidate <= {frame_header_candidate[0+:32-STREAM_WIDTH], {STREAM_WIDTH{1'b0}}};
header_mask_shifted <= {header_mask_shifted[0+:32-STREAM_WIDTH], {STREAM_WIDTH{1'b0}}};
beats_cnt <= beats_cnt + |header_mask_shifted[32-STREAM_WIDTH+:STREAM_WIDTH];
end
end
end
LAST: begin
tvalid <= 1'b1;
tstrb <= 1'b1;
tkeep <= 1'b1;
tlast <= 1'b1;
tdata <= data;
end
endcase
end
end
always_ff @(posedge clk) begin
if (!aresetn) begin
counter_rx_data <= 32'h0;
counter_rx_last <= 32'h0;
counter_half_beats <= 0;
end else begin
if (tvalid && tready && tkeep) begin
counter_rx_data <= counter_rx_data + 32'h1;
end
if (tvalid && tready && tlast) begin
counter_rx_last <= counter_rx_last + 32'h1;
end
if (mode_wire3 == 1 && ssp_csn_sync[1] && state == RECEIVE && rx_bit_cnt != 3'h0) begin
counter_half_beats <= counter_half_beats + 32'h1;
end
end
end
endmodule