osmo-e1-hardware/gateware/common/rtl/wb_arbiter.v

128 lines
2.4 KiB
Verilog

/*
* wb_arbiter.v
*
* vim: ts=4 sw=4
*
* Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module wb_arbiter #(
parameter integer N = 3,
parameter integer DW = 32,
parameter integer AW = 16,
parameter integer MW = DW / 8
)(
/* Slave buses */
input wire [(N*AW)-1:0] s_addr,
output wire [(N*DW)-1:0] s_rdata,
input wire [(N*DW)-1:0] s_wdata,
input wire [(N*MW)-1:0] s_wmsk,
input wire [ N -1:0] s_we,
input wire [ N -1:0] s_cyc,
output wire [ N -1:0] s_ack,
/* Master bus */
output reg [AW-1:0] m_addr,
input wire [DW-1:0] m_rdata,
output reg [DW-1:0] m_wdata,
output reg [MW-1:0] m_wmsk,
output reg m_we,
output wire m_cyc,
input wire m_ack,
/* Clock / Reset */
input wire clk,
input wire rst
);
// Signals
// -------
genvar i;
reg [AW-1:0] mux_addr;
reg [DW-1:0] mux_wdata;
reg [MW-1:0] mux_wmsk;
reg [N-1:0] sel_nxt;
reg [N-1:0] sel;
reg busy;
wire reselect;
// Muxing
// ------
for (i=0; i<N; i=i+1)
begin
assign s_rdata[DW*i+:DW] = sel[i] ? m_rdata : { DW{1'b0} };
assign s_ack[i] = sel[i] ? m_ack : 1'b0;
end
always @(*)
begin : mux
integer i;
mux_addr = { AW{1'b0} };
mux_wdata = { DW{1'b0} };
mux_wmsk = { MW{1'b0} };
for (i=N-1; i>=0; i=i-1) begin
mux_addr = mux_addr | (sel_nxt[i] ? s_addr[AW*i+:AW] : { AW{1'b0} });
mux_wdata = mux_wdata | (sel_nxt[i] ? s_wdata[DW*i+:DW] : { DW{1'b0} });
mux_wmsk = mux_wmsk | (sel_nxt[i] ? s_wmsk[MW*i+:MW] : { MW{1'b0} });
end
end
always @(posedge clk or posedge rst)
begin
if (rst) begin
m_addr <= { AW{1'b0} };
m_wdata <= { DW{1'b0} };
m_wmsk <= { MW{1'b0} };
m_we <= 1'b0;
end else if (reselect) begin
m_addr <= mux_addr;
m_wdata <= mux_wdata;
m_wmsk <= mux_wmsk;
m_we <= |(s_we & sel_nxt);
end
end
// Arbitration
// -----------
// Priority encoder for the next master
always @(*)
begin : prio
integer i;
sel_nxt <= 0;
for (i=N-1; i>=0; i=i-1)
if (s_cyc[i] & ~sel[i]) begin
sel_nxt <= 0;
sel_nxt[i] <= 1'b1;
end
end
// When to reselect
assign reselect = m_ack | ~busy;
// Register current master (if any)
always @(posedge clk or posedge rst)
if (rst) begin
busy <= 1'b0;
sel <= 0;
end else if (reselect) begin
busy <= |(s_cyc & ~sel);
sel <= sel_nxt;
end
assign m_cyc = busy;
endmodule // wb_arbiter