首次提交
This commit is contained in:
322
ssp_rx/src/ssp_rx.sv
Normal file
322
ssp_rx/src/ssp_rx.sv
Normal 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
|
||||
Reference in New Issue
Block a user