230 lines
7.1 KiB
Verilog
230 lines
7.1 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 cs_rise;
|
|
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];
|
|
assign cs_rise = (!cs_sync[1] && cs_sync[0]);
|
|
|
|
// 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] tx_buffer; // Holds data waiting for the next CS Low
|
|
reg [31:0] miso_shift; // The actual shifter
|
|
// 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;
|
|
end
|
|
else begin
|
|
// Clear data_ready when FSM has consumed the command
|
|
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};
|
|
if (bit_cnt == 6'd7) begin
|
|
// 8th bit received: check if read (MSB=0) or write (MSB=1)
|
|
if (recv_reg[0] == 1'b0) begin
|
|
// Read command: set data_ready after 8 bits
|
|
data_ready <= 1'b1;
|
|
end
|
|
end
|
|
else if (bit_cnt == 6'd31) begin
|
|
// 32nd bit received: write command complete
|
|
data_ready <= 1'b1;
|
|
end
|
|
bit_cnt <= bit_cnt + 1'b1;
|
|
end
|
|
else if (cs_rise) begin
|
|
if (bit_cnt == 6'd32) begin
|
|
data_ready <= 1'b1;
|
|
err_flag <= 1'b0;
|
|
bit_cnt <= 6'd0;
|
|
end
|
|
else if (bit_cnt != 0) begin
|
|
err_flag <= 1'b1;
|
|
bit_cnt <= 6'd0;
|
|
recv_reg <= 'd0;
|
|
end
|
|
end
|
|
end
|
|
if (state == DONE) begin
|
|
recv_reg <= 'd0;
|
|
end
|
|
end
|
|
end
|
|
|
|
// --- 3. MISO (Transmit) Logic ---
|
|
// Protocol: We shift out 32 bits.
|
|
// Format: [8 bit Status/Padding] + [24 bit i_rdata]
|
|
|
|
|
|
// Capture data from backend when valid
|
|
always @(posedge i_sys_clk) begin
|
|
if (!i_rst_n) begin
|
|
tx_buffer <= 32'd0;
|
|
end else begin
|
|
// If backend provides valid read data, store it.
|
|
// We pad the top 8 bits with Zeros (or you can put status flags here)
|
|
if (i_rvalid) begin
|
|
tx_buffer <= {8'h00, i_rdata};
|
|
end
|
|
end
|
|
end
|
|
|
|
// Shift data out
|
|
always @(posedge i_sys_clk) begin
|
|
if (!i_rst_n) begin
|
|
miso_shift <= 32'd0;
|
|
end else begin
|
|
if (!cs_active) begin
|
|
// Reset shifter while CS is High
|
|
miso_shift <= 32'd0;
|
|
end else if (sclk_fall) begin
|
|
// Shift on Falling Edge (Master samples on Rising)
|
|
miso_shift <= {miso_shift[30:0], 1'b0};
|
|
end
|
|
// Load register data when read completes (takes priority over shift)
|
|
if (i_rvalid) begin
|
|
miso_shift <= {8'h00, i_rdata};
|
|
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 ---
|
|
|
|
|
|
|
|
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
|
|
addr_out <= recv_reg[30:24];
|
|
data_out <= recv_reg[23:0];
|
|
wr_out <= recv_reg[31];
|
|
|
|
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 |