//(C) TechnicallyObsolete 2025 //Bus bridge from i960SA/i960SB to Wishbone bus `default_nettype none module wb_cpu_bus_i960sb( input clk_i, input rst_i, input inputs_valid_i, //To CPU input [31:0] lv_ad_i, //Technically this is AD[31:1] and D[0], but the logic is so similar that it's easier to consider it 31:0 input clk_cpu_x1, output logic [31:0] lv_ad_o, output logic lv_ad_oe, input [3:1] lv_a_i, input [1:0] lv_be_i, output logic lv_inta_out_o, input lv_dt_r_i, input lv_w_r_i, input lv_hlda_i, input lv_den_i, output logic lv_rdy_o, output logic lv_dat_dir_o, //1 = CPU to FPGA, 0 = FPGA to CPU output logic lv_dat_lvls_dis_o, output logic lv_lock_out_o, input lv_blast_i, input lv_as_i, input lv_ale_i, input lv_lock_in_i, input lv_inta_in_i, output lv_hold_o, //Bus controller output logic wb_stb_o, output logic wb_cyc_o, output logic wb_we_o, input wb_ack_i, output logic [31:0] wb_adr_o, output logic [3:0] wb_sel_o, input [31:0] wb_dat_i, output logic [31:0] wb_dat_o, output [3:0] dbg_state_o ); logic clk_cpu_d; always_ff @(posedge clk_i) begin if (rst_i) begin clk_cpu_d <= 1'b0; end else begin clk_cpu_d <= clk_cpu_x1; end end wire clk_cpu_fell; assign clk_cpu_fell = clk_cpu_x1 && (~clk_cpu_d); logic [31:0] adr_latched; logic ale_latched; logic den_b_latched; wire [31:0] adr_before_latch; assign adr_before_latch[31:4] = lv_ad_i[31:4]; assign adr_before_latch[3:1] = lv_a_i[3:1]; assign adr_before_latch[0] = 1'b0; always_ff @(posedge clk_i) begin if (rst_i) begin adr_latched <= '0; ale_latched <= 1'b0; den_b_latched <= 1'b1; end else if (inputs_valid_i) begin adr_latched <= (lv_ale_i) ? adr_before_latch : {adr_latched[31:4], lv_a_i[3:1], 1'b0}; ale_latched <= lv_ale_i; den_b_latched <= lv_den_i; end end assign lv_ad_oe = (lv_den_i == 1'b0) && (lv_dt_r_i == 1'b0); //There's an error on the PCB: //The upper 16 bits of AD[31:0] aren't bidirectional, they should be output only (A31:16 not AD31:16) //There are 330 ohm resistors in series with each pin, so worst case current will be 5/330 = 15mA/pin, 242mA total. //To avoid sinking this current, latch AD[31:16] during the address phase, and copy it back out onto the pins if a data read happens assign lv_ad_o[31:16] = adr_latched[31:16]; logic [15:0] dat_to_cpu; assign lv_ad_o[15:0] = dat_to_cpu; logic [31:0] wb_dat_i_latched; //Transaction State Machine localparam S_IDLE = 4'd0; localparam S_GET_FMT = 4'd1; localparam S_RD = 4'd2; localparam S_DONE = 4'd3; localparam S_WR = 4'd4; localparam S_RD_ACK = 4'd8; localparam S_WR_ACK = 4'd9; localparam S_DONE_ACK = 4'd10; localparam S_WR_NEXT = 4'd5; localparam S_RD_NEXT = 4'd6; localparam S_WAIT_ALE_LOW = 4'd7; logic [3:0] state_current; logic [3:0] state_next; always_ff @(posedge clk_i) begin if (rst_i) begin state_current <= S_IDLE; end else begin state_current <= state_next; end end always_comb begin case(state_current) S_IDLE: state_next = ale_latched ? S_WAIT_ALE_LOW: S_IDLE; S_WAIT_ALE_LOW: state_next = (~ale_latched) ? S_GET_FMT : S_WAIT_ALE_LOW; S_GET_FMT: begin if (clk_cpu_fell) begin if (lv_w_r_i) begin state_next = S_WR; end else begin state_next = S_RD; end end else begin state_next = S_GET_FMT; end end S_RD: begin if (wb_ack_i && (~lv_blast_i)) begin state_next = S_DONE_ACK; end else if (wb_ack_i && lv_blast_i) begin state_next = S_RD_ACK; end else begin state_next = S_RD; end end S_WR: state_next = wb_ack_i ? S_WR_ACK: S_WR; S_RD_ACK: state_next = clk_cpu_fell ? S_RD_NEXT : S_RD_ACK; S_WR_ACK: state_next = clk_cpu_fell ? S_WR_NEXT : S_WR_ACK; S_DONE_ACK: state_next = clk_cpu_fell ? S_DONE : S_DONE_ACK; S_WR_NEXT: begin if (clk_cpu_fell && (~lv_blast_i)) begin state_next = S_DONE_ACK; end else if (clk_cpu_fell && (lv_blast_i)) begin state_next = S_WR; end else begin state_next = S_WR_NEXT; end end S_RD_NEXT: state_next = clk_cpu_fell ? S_RD : S_RD_NEXT; S_DONE: state_next = clk_cpu_x1 ? S_IDLE : S_DONE; default: state_next = S_IDLE; endcase end logic [3:0] wb_sel_next; always_comb begin case({lv_a_i[1], lv_be_i}) 3'b000: wb_sel_next = 4'b0011; 3'b010: wb_sel_next = 4'b0001; 3'b001: wb_sel_next = 4'b0010; 3'b100: wb_sel_next = 4'b1100; 3'b110: wb_sel_next = 4'b0100; 3'b101: wb_sel_next = 4'b1000; default: wb_sel_next = 4'b0000; endcase end always_ff @(posedge clk_i) begin if (rst_i) begin wb_cyc_o <= 1'b0; wb_stb_o <= 1'b0; wb_sel_o <= 4'b0; wb_we_o <= 1'b0; end else begin wb_cyc_o <= (state_next != S_IDLE); wb_stb_o <= (state_next == S_RD) || (state_next == S_WR); wb_sel_o <= wb_sel_next; wb_we_o <= (state_next == S_WR) || (state_next == S_WR_NEXT); end end wire latch_end_next; wire latch_end_done; assign latch_end_next = (state_next == S_RD_NEXT) && (state_current != S_RD_NEXT); assign latch_end_done = (state_next == S_DONE) && (state_current != S_DONE); always_ff @(posedge clk_i) begin if (rst_i) begin dat_to_cpu <= '0; end else if (wb_ack_i) begin dat_to_cpu <= lv_a_i[1] ? wb_dat_i[31:16] : wb_dat_i[15:0]; end end assign wb_adr_o = {adr_latched[31:4], lv_a_i[3:2], 2'b00}; assign lv_dat_dir_o = lv_dt_r_i || (~lv_as_i); //1 = CPU to FPGA assign lv_dat_lvls_dis_o = lv_den_i && (~lv_ale_i) ; //LVLS enabled always_ff @(posedge clk_i) begin if (rst_i) begin lv_rdy_o <= 1'b1; end else begin lv_rdy_o <= ~((state_current == S_RD_NEXT) || (state_next == S_WR_NEXT) || (state_current == S_DONE)); end end assign lv_hold_o = 1'b0; assign dbg_state_o = state_current; endmodule