Files
NewInstrCalBoard_CPLD/CPLD1/BUS_Con.v
T
Jeremy Shen ed3fbd7cf5 Fix the SPI MISO logic
Modify the tb to make it easier from all aspects
2026-06-02 11:23:34 +08:00

242 lines
8.0 KiB
Verilog

module BUS_Con (
input i_sys_clk,
input i_rst_n,
// SPI Interface
input i_sclk,
input i_mosi,
input i_cs,
output o_miso, // <-- MISO Logic added here
// Local Interface
output o_rvalid, // (Typo in original: o_rvalid)
input i_rvalid, // Valid signal from Backend (Data is ready)
input i_wvalid,
output o_wready,
input i_reg_busy,
input [23:0] i_rdata, // Data from Backend to send to Master
output o_wr,
output o_cmd_valid,
output [23:0] o_data,
output [6:0] o_addr,
output o_err
);
// --- 1. Synchronization ---
reg [1:0] cs_sync;
reg [1:0] sclk_sync;
reg [1:0] mosi_sync;
wire cs_active;
wire sclk_rise; // For Sampling MOSI
wire sclk_fall; // For Shifting MISO
wire mosi_data;
always @(posedge i_sys_clk) begin
if (!i_rst_n) begin
cs_sync <= 2'b11;
sclk_sync <= 2'b00;
mosi_sync <= 2'b00;
end else begin
cs_sync <= {cs_sync[0], i_cs};
sclk_sync <= {sclk_sync[0], i_sclk};
mosi_sync <= {mosi_sync[0], i_mosi};
end
end
assign cs_active = ~cs_sync[1];
// SPI Mode 0: Sample MOSI on Rising, Shift MISO on Falling
assign sclk_rise = (!sclk_sync[1] && sclk_sync[0]);
assign sclk_fall = (sclk_sync[1] && !sclk_sync[0]); // Logic for MISO
assign mosi_data = mosi_sync[1];
// --- 2. MOSI (Receive) Logic ---
reg [31:0] recv_reg;
reg [5:0] bit_cnt;
reg data_ready;
reg err_flag;
reg [1:0] state;
//
reg [31:0] miso_shift; // The actual shifter
reg wr_flag;// Flag: 1=W/0=R
reg miso_loaded;
// Output Registers
reg [6:0] addr_out;
reg [23:0] data_out;
reg wr_out;
reg cmd_valid_out;
//
localparam IDLE = 2'b00;
localparam CMD_SENT = 2'b01;
localparam WAIT_BUSY = 2'b10;
localparam DONE = 2'b11;
always @(posedge i_sys_clk) begin
if (!i_rst_n) begin
recv_reg <= 32'd0;
bit_cnt <= 6'd0;
data_ready <= 1'b0;
err_flag <= 1'b0;
wr_flag <= 1'b1;
end
else begin
// Clear data_ready when FSM has consumed the command (CMD_SENT = consumed)
if (state == CMD_SENT) begin
data_ready <= 1'b0;
end
if (cs_active) begin
if (sclk_rise) begin
recv_reg <= {recv_reg[30:0], mosi_data};
bit_cnt <= bit_cnt + 1'b1;
// After 8 bits: check WR bit (recv_reg[7] = first bit received)
// Read (WR=0): set data_ready early so MISO data is prepared
// Write (WR=1): wait for all 32 bits
if (bit_cnt == 'd7) begin
if (recv_reg[6] == 1'b0) begin
// Read command: signal ready after address received
data_ready <= 1'b1;
wr_flag <= 1'b0;
end
else begin
wr_flag <= 1'b1;
end
end
else if (bit_cnt == 6'd31) begin
// Write command complete (32 bits received)
data_ready <= 1'b1;
end
end
else if (!cs_active) begin
data_ready <= 1'b0;
if (bit_cnt == 6'd31) begin
err_flag <= 1'b0;
bit_cnt <= 6'd0;
end
else if (bit_cnt != 0 && bit_cnt != 6'd32) begin
err_flag <= 1'b1;
bit_cnt <= 6'd0;
recv_reg <= 'd0;
end
end
end
else begin
// CS high: reset state
bit_cnt <= 6'd0;
recv_reg <= 'd0;
data_ready <= 1'b0;
end
end
end
// --- 3. MISO (Transmit) Logic ---
// Protocol: We shift out 32 bits.
// Format: [8'h00 padding] + [24 bit i_rdata]
// Shift MSB out on each falling edge of SCLK (master samples on rising)
// Consolidated: load on first rising edge, shift on falling edges
always @(posedge i_sys_clk) begin
if (!i_rst_n) begin
miso_shift <= 32'd0;
miso_loaded <= 1'b0;
end
else begin
if (!cs_active) begin
// Reset shifter while CS is High
miso_shift <= 32'd0;
end
else begin
if (i_rvalid && (bit_cnt == 'd8)) begin
miso_shift <= {i_rdata,8'd0};
miso_loaded <= 1'b1;
end
if (miso_loaded && sclk_fall) begin
// Shift out MSB on falling edges (data valid before master's rising-edge sample)
if (bit_cnt > 'd8) begin
miso_shift <= {miso_shift[30:0], 1'b0};
end
end
end
end
end
// Tri-state MISO when CS is high (optional, usually good practice)
// If your board doesn't need tristate, just use: assign o_miso = miso_shift[31];
assign o_miso = (cs_active) ? miso_shift[31] : 1'bz;
// --- 4. Register Control FSM ---
// Command format: [WR:1b][Addr:7b][Data:24b] = 32 bits
// recv_reg layout after 32 bits: {WR, Addr[6:0], Data[23:0]}
always @(posedge i_sys_clk) begin
if (!i_rst_n) begin
state <= IDLE;
cmd_valid_out <= 1'b0;
addr_out <= 7'd0;
data_out <= 24'd0;
wr_out <= 1'b0;
end else begin
case (state)
IDLE: begin
if (data_ready) begin
// Extract fields from received command
if (wr_flag == 1'b1) begin
addr_out <= recv_reg[30:24];
data_out <= recv_reg[23:0];
wr_out <= recv_reg[31];
end
else begin
addr_out <= recv_reg[6:0];
wr_out <= recv_reg[7];
end
if (!i_reg_busy) begin
cmd_valid_out <= 1'b1;
state <= CMD_SENT;
end else begin
state <= WAIT_BUSY;
end
end
end
WAIT_BUSY: begin
if (!i_reg_busy) begin
cmd_valid_out <= 1'b1;
state <= CMD_SENT;
end
end
CMD_SENT: begin
cmd_valid_out <= 1'b0;
state <= DONE;
end
DONE: begin
if (!i_reg_busy) begin
state <= IDLE;
addr_out <= 'd0;
data_out <= 'd0;
wr_out <= 'd0;
end
end
endcase
end
end
assign o_addr = addr_out;
assign o_data = data_out;
assign o_wr = wr_out;
assign o_cmd_valid = cmd_valid_out;
assign o_err = err_flag;
assign o_wready = (state == IDLE);
// Pass through unused signal or hook it up if needed
assign o_rvalid = i_rvalid;
endmodule