Many IPs on Opencores.org use WISHBONE bus. Opencores provides AHB to WISHBONE and WISHBONE to AHB protocol conversion designs. Here is a similar solution.
AHB single read/write transaction is
WISHBONE single read/write transaction is
Comparing these two, we can see AHB separates address phase with data phase while WISHBONE keeps address and data valid at the same time.
AHB burst read/write transaction is
WISHBONE burst read/write transaction is
Out of above two, we can get the same observation that AHB separates address phase with data phase while WISHBONE keeps address and data valid at the same time.
An AHB to WISHBONE bridge diagram is
The corresponding Verilog code is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
`include "timescale.v" module s_ahb2wb( //ahb HCLK, HRESETn, sHADDR, sHWDATA, sHWRITE, sHREADY, sHSIZE, sHBURST, sHSEL, sHTRANS, sHRDATA, sHRESP, sHREADYIN, //to wishbone to_wb_dat_i, to_wb_adr_i, to_wb_sel_i, to_wb_we_i, to_wb_cyc_i, to_wb_stb_i, from_wb_dat_o, from_wb_ack_o, from_wb_err_o ); input HCLK; input HRESETn; input [13:0] sHADDR; input [31:0] sHWDATA; input sHWRITE; input [2:0] sHSIZE; input [2:0] sHBURST; input sHSEL; input [1:0] sHTRANS; input sHREADYIN; output sHREADY; output [31:0] sHRDATA; output [1:0] sHRESP; output [31:0] to_wb_dat_i; output [11:2] to_wb_adr_i; output [3 :0] to_wb_sel_i; output to_wb_we_i; output to_wb_cyc_i; output to_wb_stb_i; input [31:0] from_wb_dat_o; input from_wb_ack_o; input from_wb_err_o; ///////////////////////////////////////////////////// reg [1:0] NextsHRESP; reg [1:0] isHRESP; reg Nextto_wb_cyc_i; reg ito_wb_cyc_i; reg Nextto_wb_stb_i; reg ito_wb_stb_i; reg [3:0] Nextto_wb_sel_i; reg [3:0] ito_wb_sel_i; reg [13:0] Nextto_wb_adr_i; reg [13:0] ito_wb_adr_i; reg Nextto_wb_we_i; reg ito_wb_we_i; reg adr_valid; ///////////////////////////////////////////////////// assign to_wb_adr_i = ito_wb_adr_i[11:2]; assign to_wb_we_i = ito_wb_we_i; assign to_wb_dat_i = sHWDATA; assign to_wb_sel_i = ito_wb_sel_i; assign sHRDATA = from_wb_dat_o; assign sHREADY = (to_wb_stb_i) ? from_wb_ack_o : 1'b1; assign sHRESP = (from_wb_err_o) ? 2'b01 : isHRESP; assign to_wb_cyc_i = ito_wb_cyc_i; assign to_wb_stb_i = ito_wb_stb_i; always @(sHSEL or sHADDR) if(!sHSEL) adr_valid = 1'b0; else if(sHADDR <= 13'h50 || (sHADDR <=13'h800 && sHADDR >= 13'h400)) adr_valid = 1'b1; else adr_valid = 1'b0; always @(*) begin NextsHRESP = isHRESP; Nextto_wb_cyc_i = ito_wb_cyc_i; Nextto_wb_stb_i = ito_wb_stb_i; Nextto_wb_adr_i = ito_wb_adr_i; Nextto_wb_we_i = ito_wb_we_i; Nextto_wb_sel_i = ito_wb_sel_i; if(sHSEL) begin if(sHREADYIN) begin if(sHSIZE != 3'b010 || !adr_valid) begin Nextto_wb_we_i = 1'b0; Nextto_wb_cyc_i = 1'b0; Nextto_wb_stb_i = 1'b0; Nextto_wb_adr_i = 14'b0; Nextto_wb_sel_i = 4'b0; if(sHTRANS == 2'b00) NextsHRESP = 2'b00; else NextsHRESP = 2'b01; end else begin case(sHTRANS) 2'b00: begin NextsHRESP = 2'b00; Nextto_wb_cyc_i = 1'b0; Nextto_wb_stb_i = 1'b0; Nextto_wb_adr_i = 14'b0; Nextto_wb_sel_i = 4'b0; end 2'b01: begin Nextto_wb_cyc_i = 1'b1; Nextto_wb_stb_i = 1'b0; end 2'b10,2'b11: begin NextsHRESP = 2'b00; Nextto_wb_cyc_i = 1'b1; Nextto_wb_stb_i = 1'b1; Nextto_wb_adr_i = sHADDR; Nextto_wb_we_i = sHWRITE; Nextto_wb_sel_i = 4'b1111; end endcase end end else begin end end else begin NextsHRESP = 2'b00; Nextto_wb_we_i = 1'b0; Nextto_wb_cyc_i = 1'b0; Nextto_wb_stb_i = 1'b0; Nextto_wb_adr_i = 14'b0; Nextto_wb_sel_i = 3'b0; end end always @(posedge HCLK or negedge HRESETn) begin if(!HRESETn) begin isHRESP <= 2'b00; ito_wb_cyc_i <= 1'b0; ito_wb_stb_i <= 1'b0; ito_wb_adr_i <= 12'b0; ito_wb_we_i <= 1'b0; ito_wb_sel_i <= 4'b0; end else begin isHRESP <= NextsHRESP; ito_wb_cyc_i <= Nextto_wb_cyc_i; ito_wb_stb_i <= Nextto_wb_stb_i; ito_wb_adr_i <= Nextto_wb_adr_i; ito_wb_we_i <= Nextto_wb_we_i; ito_wb_sel_i <= Nextto_wb_sel_i; end end endmodule |
A WISHBONE to AHB bridge diagram is
The corresponding Verilog code is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
`include "timescale.v" module m_wb2ahb( ////ahb HCLK, HRESETn, mHSIZE, mHRDATA, mHRESP, mHREADY, mHGRANT, mHWRITE, mHBURST, mHADDR, mHTRANS, mHWDATA, //mHPROT, HBUSREQ, //HLOCK, ////to wishbone from_m_wb_adr_o, from_m_wb_sel_o, from_m_wb_we_o, from_m_wb_dat_o, from_m_wb_cyc_o, from_m_wb_stb_o, to_m_wb_ack_i, to_m_wb_err_i, to_m_wb_dat_i, from_m_wb_cti_o, from_m_wb_bte_o ); input HCLK; input HRESETn; input [31:0] mHRDATA; input [1:0] mHRESP; input mHREADY; input mHGRANT; output [2:0] mHSIZE; output mHWRITE; output [2:0] mHBURST; output [31:0] mHADDR; output [1:0] mHTRANS; output [31:0] mHWDATA; output HBUSREQ; //output HLOCK; //output [3:0] mHPROT; input [31:0] from_m_wb_adr_o; input [3:0] from_m_wb_sel_o; input from_m_wb_we_o; input [31:0] from_m_wb_dat_o; input from_m_wb_cyc_o; input from_m_wb_stb_o; output to_m_wb_ack_i; output to_m_wb_err_i; output [31:0] to_m_wb_dat_i; input [2:0] from_m_wb_cti_o; input [1:0] from_m_wb_bte_o; ////////////////////////////////// parameter [1:0] IDLE = 2'b00, BUSY = 2'b01, NONSEQ = 2'b10, SEQ = 2'b11; ////////////////////////////////// reg ackmask; reg ctrlstart; wire isburst; ////////////////////////////////// reg test_askmask; always @(!HRESETn) if(!HRESETn) begin test_askmask <= 1'b1; end task cycle_response; input ack_err_rty_resp; begin test_askmask <= ack_err_rty_resp; end endtask ////////////////////////////////// assign isburst = (from_m_wb_cti_o == 3'b000) ? 1'b0 : 1'b1; assign to_m_wb_dat_i = mHRDATA ; assign to_m_wb_ack_i = ackmask & mHREADY & from_m_wb_stb_o & test_askmask; assign mHADDR = (~isburst || (ctrlstart && !ackmask) || !ctrlstart) ? from_m_wb_adr_o : from_m_wb_adr_o + 3'b100; assign mHWDATA = from_m_wb_dat_o; assign mHSIZE = 3'b010; //word assign mHBURST = (ctrlstart && (from_m_wb_cti_o == 3'b010)) ? 3'b011 : 3'b000; assign HBUSREQ = (isburst) ? (from_m_wb_cti_o == 3'b010) : (from_m_wb_stb_o && ~ackmask) ; assign mHWRITE = from_m_wb_we_o; assign mHTRANS = (ctrlstart && !ackmask) ? NONSEQ : ( (from_m_wb_cti_o == 3'b010 && ctrlstart) ? SEQ : IDLE ); assign to_m_wb_err_i = (mHRESP == 2'b00) ? 1'b0 : 1'b1; always @(posedge HCLK or negedge HRESETn) begin if(!HRESETn) ctrlstart <= 1'b0; else if(!HBUSREQ) ctrlstart <= 1'b0; else if(mHGRANT && mHREADY && !ctrlstart) ctrlstart <= 1'b1; else if(ctrlstart) ctrlstart <= 1'b1; else ctrlstart <= 1'b0; end always @(posedge HCLK or negedge HRESETn) begin if(!HRESETn) ackmask <= 1'b0; else if(!from_m_wb_stb_o) ackmask <= 1'b0; else if(!ctrlstart && !ackmask) ackmask <= 1'b0; else if(ctrlstart && !to_m_wb_ack_i && mHREADY) ackmask <= 1'b1; else if(to_m_wb_ack_i && !isburst) ackmask <= 1'b0; else if(from_m_wb_cti_o == 3'b111 && mHREADY) ackmask <= 1'b0; else ackmask <= 1'b1; end endmodule |
As a bonus, here is an AHB to PCI bridge Verilog code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
module ahb_to_pci ( hclk, hresetn, haddr, hwdata, hwrite, hready_i, hready_o, hsize, hburst, hsel, htrans, hrdata, hresp, pci_clk, pci_rstn, pci_cben, // C/BE[3:0]# Bus Command & Byte Enables pci_framen, // FRAME# PCI Frame signal than indicates the beginning and duration of a bus operation pci_irdyn, // IRDY# Initiator Ready pci_ad, // AD[31:0] Address & Data pci_par, // PAR Parity is even parity across AD[31:0] and C/BE[3:0]# pci_serrn, // PCI System error. pci_perrn, // PCI Parity error. pci_devseln, pci_trdyn, // TRDY# Target Ready pci_stopn, pci_idsel ); input hclk; input hresetn; input [31:0] haddr; input [31:0] hwdata; input hwrite; input hready_i; output hready_o; input [2:0] hsize; input [2:0] hburst; input hsel; input [1:0] htrans; output [31:0] hrdata; output [1:0] hresp; output pci_clk; output pci_rstn; output [3:0] pci_cben; output pci_framen; output reg pci_irdyn; inout [31:0] pci_ad; inout pci_par; input pci_serrn; input pci_perrn; input pci_devseln; input pci_trdyn; input pci_stopn; output pci_idsel; wire address_phase; wire wdata_oen; reg wdata_phase; wire pci_ad_oen; reg pci_par_oen; wire [31:0] pci_ad_out; reg pci_par_out; wire [31:0] pci_addr; wire cfg; wire [3:0] WRITE; wire [3:0] READ; reg trdy_delay; reg trdy_window; parameter [1:0] IDLE = 2'b00, BUSY = 2'b01, NONSEQ = 2'b10, SEQ = 2'b11, OKAY = 2'b00, ERROR = 2'b01; parameter [2:0] SINGLE = 3'b000, INCR = 3'b001, INCR4 = 3'b011, INCR8 = 3'b101, INCR16 = 3'b111; parameter [3:0] MREAD = 4'b0110, MWRITE = 4'b0111, CREAD = 4'b1010, CWRITE = 4'b1011; assign pci_clk = hclk; assign pci_rstn = hresetn; assign hresp = OKAY; assign cfg = hsel && haddr[15] && haddr[14] && haddr[13] && haddr[12]; assign WRITE = cfg ? CWRITE : MWRITE; assign READ = cfg? CREAD : MREAD; assign address_phase = hsel && (htrans == NONSEQ) && hready_i; assign pci_idsel = cfg ? address_phase : 1'b0; assign pci_framen = (hburst == SINGLE) ? !address_phase : !(address_phase || (htrans[0] && hsel) ); assign pci_cben = address_phase ? (hwrite ? WRITE : READ) : 4'b0000; always @(posedge hclk or negedge hresetn) begin if(!hresetn) pci_irdyn <= 1'b1; else if (address_phase || ((htrans == SEQ) && hsel) ) pci_irdyn <= 1'b0; else if ( (htrans == BUSY || htrans == IDLE) && hready_o ) pci_irdyn <= 1'b1; else pci_irdyn <= pci_irdyn; end always @(posedge hclk or negedge hresetn) begin if(!hresetn) wdata_phase <= 1'b0; else if (hsel && hwrite) wdata_phase <= 1'b1; else if (!hsel) wdata_phase <= 1'b0; else wdata_phase <= wdata_phase; end assign wdata_oen = !(wdata_phase && hsel); assign pci_ad_oen = !(!wdata_oen || address_phase); assign pci_addr = {3'b000,pci_idsel,12'b0,haddr[15:0]}; //pci_addr assign pci_ad_out = address_phase ? pci_addr : hwdata; bufif0 adout[31:0] (pci_ad,pci_ad_out,pci_ad_oen); assign hrdata = pci_ad; always @(posedge hclk or negedge hresetn) begin if(!hresetn) pci_par_oen <= 1'b1; else pci_par_oen <= pci_ad_oen; end always @(posedge hclk or negedge hresetn) begin if(!hresetn) pci_par_out <= 1'b0; else pci_par_out <= (^pci_cben) ^ (^pci_ad_out); end bufif0 parout (pci_par,pci_par_out,pci_par_oen); always @(posedge hclk or negedge hresetn) begin if(!hresetn) trdy_delay <= 1'b0; else if(address_phase) trdy_delay <= 1'b1; else trdy_delay <= 1'b0; end always @(posedge hclk or negedge hresetn) begin if(!hresetn) trdy_window <= 1'b0; else if(trdy_delay) trdy_window <= 1'b1; else if(!hsel || htrans == NONSEQ) trdy_window <= 1'b0; else trdy_window <= 1'b1; end assign hready_o = trdy_delay ? 1'b0 : (trdy_window && hsel ? !pci_trdyn : 1'b1); endmodule |
Wishbone is not popular as AMBA dominates in SoC. Many IPs on opencores use wishbone so these bridges come in handy. But if you can, I’d say you may want to replace wishbone with AMBA totally especially for burst operation.