流水线 CPU 的设计与实现 - 仲裁器
简介
在实现流水线 CPU 的时候,我们会遇到这样一个问题:IF 段和 MEM 段有可能会同时进行请求。
我们肯定不能把两个请求同时放到我们的 wishbone 总线上——这样肯定会冲突。
因此,我们需要一个新的功能部件——仲裁器来解决这个冲突问题。
有些同学可能想起,我们在实验 2——通过例子学硬件描述语言中曾经给出过两种仲裁器的实现:
请同学们先阅读上面两个例子的内容,再来看接下来的 wishbone 仲裁器的部分。
Wishbone 仲裁器
在这里,我们给出一个 wishbone 仲裁器的实现。
arbiter.v
/*
Copyright (c) 2014-2021 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Arbiter module
*/
module arbiter #
(
parameter PORTS = 4,
// select round robin arbitration
parameter ARB_TYPE_ROUND_ROBIN = 0,
// blocking arbiter enable
parameter ARB_BLOCK = 0,
// block on acknowledge assert when nonzero, request deassert when 0
parameter ARB_BLOCK_ACK = 1,
// LSB priority selection
parameter ARB_LSB_HIGH_PRIORITY = 0
)
(
input wire clk,
input wire rst,
input wire [PORTS-1:0] request,
input wire [PORTS-1:0] acknowledge,
output wire [PORTS-1:0] grant,
output wire grant_valid,
output wire [$clog2(PORTS)-1:0] grant_encoded
);
reg [PORTS-1:0] grant_reg = 0, grant_next;
reg grant_valid_reg = 0, grant_valid_next;
reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next;
assign grant_valid = grant_valid_reg;
assign grant = grant_reg;
assign grant_encoded = grant_encoded_reg;
wire request_valid;
wire [$clog2(PORTS)-1:0] request_index;
wire [PORTS-1:0] request_mask;
priority_encoder #(
.WIDTH(PORTS),
.LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
)
priority_encoder_inst (
.input_unencoded(request),
.output_valid(request_valid),
.output_encoded(request_index),
.output_unencoded(request_mask)
);
reg [PORTS-1:0] mask_reg = 0, mask_next;
wire masked_request_valid;
wire [$clog2(PORTS)-1:0] masked_request_index;
wire [PORTS-1:0] masked_request_mask;
priority_encoder #(
.WIDTH(PORTS),
.LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
)
priority_encoder_masked (
.input_unencoded(request & mask_reg),
.output_valid(masked_request_valid),
.output_encoded(masked_request_index),
.output_unencoded(masked_request_mask)
);
always @* begin
grant_next = 0;
grant_valid_next = 0;
grant_encoded_next = 0;
mask_next = mask_reg;
if (ARB_BLOCK && !ARB_BLOCK_ACK && grant_reg & request) begin
// granted request still asserted; hold it
grant_valid_next = grant_valid_reg;
grant_next = grant_reg;
grant_encoded_next = grant_encoded_reg;
end else if (ARB_BLOCK && ARB_BLOCK_ACK && grant_valid && !(grant_reg & acknowledge)) begin
// granted request not yet acknowledged; hold it
grant_valid_next = grant_valid_reg;
grant_next = grant_reg;
grant_encoded_next = grant_encoded_reg;
end else if (request_valid) begin
if (ARB_TYPE_ROUND_ROBIN) begin
if (masked_request_valid) begin
grant_valid_next = 1;
grant_next = masked_request_mask;
grant_encoded_next = masked_request_index;
if (ARB_LSB_HIGH_PRIORITY) begin
mask_next = {PORTS{1'b1}} << (masked_request_index + 1);
end else begin
mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index);
end
end else begin
grant_valid_next = 1;
grant_next = request_mask;
grant_encoded_next = request_index;
if (ARB_LSB_HIGH_PRIORITY) begin
mask_next = {PORTS{1'b1}} << (request_index + 1);
end else begin
mask_next = {PORTS{1'b1}} >> (PORTS - request_index);
end
end
end else begin
grant_valid_next = 1;
grant_next = request_mask;
grant_encoded_next = request_index;
end
end
end
always @(posedge clk) begin
if (rst) begin
grant_reg <= 0;
grant_valid_reg <= 0;
grant_encoded_reg <= 0;
mask_reg <= 0;
end else begin
grant_reg <= grant_next;
grant_valid_reg <= grant_valid_next;
grant_encoded_reg <= grant_encoded_next;
mask_reg <= mask_next;
end
end
endmodule
priority_encoder.v
/*
Copyright (c) 2014-2021 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Priority encoder module
*/
module priority_encoder #
(
parameter WIDTH = 4,
// LSB priority selection
parameter LSB_HIGH_PRIORITY = 0
)
(
input wire [WIDTH-1:0] input_unencoded,
output wire output_valid,
output wire [$clog2(WIDTH)-1:0] output_encoded,
output wire [WIDTH-1:0] output_unencoded
);
parameter LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1;
parameter W = 2**LEVELS;
// pad input to even power of two
wire [W-1:0] input_padded = {{W-WIDTH{1'b0}}, input_unencoded};
wire [W/2-1:0] stage_valid[LEVELS-1:0];
wire [W/2-1:0] stage_enc[LEVELS-1:0];
generate
genvar l, n;
// process input bits; generate valid bit and encoded bit for each pair
for (n = 0; n < W/2; n = n + 1) begin : loop_in
assign stage_valid[0][n] = |input_padded[n*2+1:n*2];
if (LSB_HIGH_PRIORITY) begin
// bit 0 is highest priority
assign stage_enc[0][n] = !input_padded[n*2+0];
end else begin
// bit 0 is lowest priority
assign stage_enc[0][n] = input_padded[n*2+1];
end
end
// compress down to single valid bit and encoded bus
for (l = 1; l < LEVELS; l = l + 1) begin : loop_levels
for (n = 0; n < W/(2*2**l); n = n + 1) begin : loop_compress
assign stage_valid[l][n] = |stage_valid[l-1][n*2+1:n*2];
if (LSB_HIGH_PRIORITY) begin
// bit 0 is highest priority
assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+0] ? {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]} : {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]};
end else begin
// bit 0 is lowest priority
assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+1] ? {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]} : {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]};
end
end
end
endgenerate
assign output_valid = stage_valid[LEVELS-1];
assign output_encoded = stage_enc[LEVELS-1];
assign output_unencoded = 1 << output_encoded;
endmodule
wb_arbiter_2.v
/*
Copyright (c) 2015-2016 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1 ns / 1 ps
/*
* Wishbone 2 port arbiter
*/
module wb_arbiter_2 #
(
parameter DATA_WIDTH = 32, // width of data bus in bits (8, 16, 32, or 64)
parameter ADDR_WIDTH = 32, // width of address bus in bits
parameter SELECT_WIDTH = (DATA_WIDTH/8), // width of word select bus (1, 2, 4, or 8)
parameter ARB_TYPE_ROUND_ROBIN = 0, // select round robin arbitration
parameter ARB_LSB_HIGH_PRIORITY = 1 // LSB priority selection
)
(
input wire clk,
input wire rst,
/*
* Wishbone master 0 input
*/
input wire [ADDR_WIDTH-1:0] wbm0_adr_i, // ADR_I() address input
input wire [DATA_WIDTH-1:0] wbm0_dat_i, // DAT_I() data in
output wire [DATA_WIDTH-1:0] wbm0_dat_o, // DAT_O() data out
input wire wbm0_we_i, // WE_I write enable input
input wire [SELECT_WIDTH-1:0] wbm0_sel_i, // SEL_I() select input
input wire wbm0_stb_i, // STB_I strobe input
output wire wbm0_ack_o, // ACK_O acknowledge output
output wire wbm0_err_o, // ERR_O error output
output wire wbm0_rty_o, // RTY_O retry output
input wire wbm0_cyc_i, // CYC_I cycle input
/*
* Wishbone master 1 input
*/
input wire [ADDR_WIDTH-1:0] wbm1_adr_i, // ADR_I() address input
input wire [DATA_WIDTH-1:0] wbm1_dat_i, // DAT_I() data in
output wire [DATA_WIDTH-1:0] wbm1_dat_o, // DAT_O() data out
input wire wbm1_we_i, // WE_I write enable input
input wire [SELECT_WIDTH-1:0] wbm1_sel_i, // SEL_I() select input
input wire wbm1_stb_i, // STB_I strobe input
output wire wbm1_ack_o, // ACK_O acknowledge output
output wire wbm1_err_o, // ERR_O error output
output wire wbm1_rty_o, // RTY_O retry output
input wire wbm1_cyc_i, // CYC_I cycle input
/*
* Wishbone slave output
*/
output wire [ADDR_WIDTH-1:0] wbs_adr_o, // ADR_O() address output
input wire [DATA_WIDTH-1:0] wbs_dat_i, // DAT_I() data in
output wire [DATA_WIDTH-1:0] wbs_dat_o, // DAT_O() data out
output wire wbs_we_o, // WE_O write enable output
output wire [SELECT_WIDTH-1:0] wbs_sel_o, // SEL_O() select output
output wire wbs_stb_o, // STB_O strobe output
input wire wbs_ack_i, // ACK_I acknowledge input
input wire wbs_err_i, // ERR_I error input
input wire wbs_rty_i, // RTY_I retry input
output wire wbs_cyc_o // CYC_O cycle output
);
wire [1:0] request;
wire [1:0] grant;
wire grant_valid;
assign request[0] = wbm0_cyc_i;
assign request[1] = wbm1_cyc_i;
wire wbm0_sel = grant[0] & grant_valid;
wire wbm1_sel = grant[1] & grant_valid;
// master 0
assign wbm0_dat_o = wbs_dat_i;
assign wbm0_ack_o = wbs_ack_i & wbm0_sel;
assign wbm0_err_o = wbs_err_i & wbm0_sel;
assign wbm0_rty_o = wbs_rty_i & wbm0_sel;
// master 1
assign wbm1_dat_o = wbs_dat_i;
assign wbm1_ack_o = wbs_ack_i & wbm1_sel;
assign wbm1_err_o = wbs_err_i & wbm1_sel;
assign wbm1_rty_o = wbs_rty_i & wbm1_sel;
// slave
assign wbs_adr_o = wbm0_sel ? wbm0_adr_i :
wbm1_sel ? wbm1_adr_i :
{ADDR_WIDTH{1'b0}};
assign wbs_dat_o = wbm0_sel ? wbm0_dat_i :
wbm1_sel ? wbm1_dat_i :
{DATA_WIDTH{1'b0}};
assign wbs_we_o = wbm0_sel ? wbm0_we_i :
wbm1_sel ? wbm1_we_i :
1'b0;
assign wbs_sel_o = wbm0_sel ? wbm0_sel_i :
wbm1_sel ? wbm1_sel_i :
{SELECT_WIDTH{1'b0}};
assign wbs_stb_o = wbm0_sel ? wbm0_stb_i :
wbm1_sel ? wbm1_stb_i :
1'b0;
assign wbs_cyc_o = wbm0_sel ? 1'b1 :
wbm1_sel ? 1'b1 :
1'b0;
// arbiter instance
arbiter #(
.PORTS(2),
.ARB_TYPE_ROUND_ROBIN(ARB_TYPE_ROUND_ROBIN),
.ARB_BLOCK(1),
.ARB_BLOCK_ACK(0),
.ARB_LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
)
arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded()
);
endmodule
使用说明
请同学们将这 3 个文件加入到你的工程当中,然后例化 wb_arbiter_2 即可。
接线时将 wishbone 总线的对应信号接上就可以正常使用仲裁器了。
请注意 arbiter 中优先级的处理。
ARB_TYPE_ROUND_ROBIN
为是否使用循环优先级仲裁,实验中请不要使用循环优先级,因此请设置为 0
ARB_LSB_HIGH_PRIORITY
为是否让小端(0)为高优先级。由于我们这里是一个双口的 arbiter,而且不使用循环优先级。因此设置为 0 的时候 1 号口的优先级更高,设置为 1 的时候 0 号口的优先级会更高。同学们可以按需设置每个口的优先级进行连线。
如果觉得这个仲裁器太过复杂,同学们也可以根据 lab2 中仲裁器的相关内容自己设计 wishbone 仲裁器。
如果希望使用拥有更多 master 接口的 arbiter,则请参考 SoC 设计 一节有关 wb_mux 的内容使用下面的 python 脚本生成 verilog 并加入项目即可。
wb_arbiter.py
#!/usr/bin/env python
"""
Generates a Wishbone arbiter with the specified number of ports
"""
from __future__ import print_function
import argparse
import math
from jinja2 import Template
def main():
parser = argparse.ArgumentParser(description=__doc__.strip())
parser.add_argument('-p', '--ports', type=int, default=2, help="number of ports")
parser.add_argument('-n', '--name', type=str, help="module name")
parser.add_argument('-o', '--output', type=str, help="output file name")
args = parser.parse_args()
try:
generate(**args.__dict__)
except IOError as ex:
print(ex)
exit(1)
def generate(ports=2, name=None, output=None):
if name is None:
name = "wb_arbiter_{0}".format(ports)
if output is None:
output = name + ".v"
print("Opening file '{0}'...".format(output))
output_file = open(output, 'w')
print("Generating {0} port Wishbone arbiter {1}...".format(ports, name))
select_width = int(math.ceil(math.log(ports, 2)))
t = Template(u"""/*
Copyright (c) 2015-2016 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1 ns / 1 ps
/*
* Wishbone {{n}} port arbiter
*/
module {{name}} #
(
parameter DATA_WIDTH = 32, // width of data bus in bits (8, 16, 32, or 64)
parameter ADDR_WIDTH = 32, // width of address bus in bits
parameter SELECT_WIDTH = (DATA_WIDTH/8), // width of word select bus (1, 2, 4, or 8)
parameter ARB_TYPE_ROUND_ROBIN = 0, // select round robin arbitration
parameter ARB_LSB_HIGH_PRIORITY = 1 // LSB priority selection
)
(
input wire clk,
input wire rst,
{%- for p in ports %}
/*
* Wishbone master {{p}} input
*/
input wire [ADDR_WIDTH-1:0] wbm{{p}}_adr_i, // ADR_I() address input
input wire [DATA_WIDTH-1:0] wbm{{p}}_dat_i, // DAT_I() data in
output wire [DATA_WIDTH-1:0] wbm{{p}}_dat_o, // DAT_O() data out
input wire wbm{{p}}_we_i, // WE_I write enable input
input wire [SELECT_WIDTH-1:0] wbm{{p}}_sel_i, // SEL_I() select input
input wire wbm{{p}}_stb_i, // STB_I strobe input
output wire wbm{{p}}_ack_o, // ACK_O acknowledge output
output wire wbm{{p}}_err_o, // ERR_O error output
output wire wbm{{p}}_rty_o, // RTY_O retry output
input wire wbm{{p}}_cyc_i, // CYC_I cycle input
{%- endfor %}
/*
* Wishbone slave output
*/
output wire [ADDR_WIDTH-1:0] wbs_adr_o, // ADR_O() address output
input wire [DATA_WIDTH-1:0] wbs_dat_i, // DAT_I() data in
output wire [DATA_WIDTH-1:0] wbs_dat_o, // DAT_O() data out
output wire wbs_we_o, // WE_O write enable output
output wire [SELECT_WIDTH-1:0] wbs_sel_o, // SEL_O() select output
output wire wbs_stb_o, // STB_O strobe output
input wire wbs_ack_i, // ACK_I acknowledge input
input wire wbs_err_i, // ERR_I error input
input wire wbs_rty_i, // RTY_I retry input
output wire wbs_cyc_o // CYC_O cycle output
);
wire [{{n-1}}:0] request;
wire [{{n-1}}:0] grant;
{% for p in ports %}
assign request[{{p}}] = wbm{{p}}_cyc_i;
{%- endfor %}
{% for p in ports %}
wire wbm{{p}}_sel = grant[{{p}}] & grant_valid;
{%- endfor %}
{%- for p in ports %}
// master {{p}}
assign wbm{{p}}_dat_o = wbs_dat_i;
assign wbm{{p}}_ack_o = wbs_ack_i & wbm{{p}}_sel;
assign wbm{{p}}_err_o = wbs_err_i & wbm{{p}}_sel;
assign wbm{{p}}_rty_o = wbs_rty_i & wbm{{p}}_sel;
{%- endfor %}
// slave
assign wbs_adr_o = {% for p in ports %}wbm{{p}}_sel ? wbm{{p}}_adr_i :
{% endfor %}{ADDR_WIDTH{1'b0}};
assign wbs_dat_o = {% for p in ports %}wbm{{p}}_sel ? wbm{{p}}_dat_i :
{% endfor %}{DATA_WIDTH{1'b0}};
assign wbs_we_o = {% for p in ports %}wbm{{p}}_sel ? wbm{{p}}_we_i :
{% endfor %}1'b0;
assign wbs_sel_o = {% for p in ports %}wbm{{p}}_sel ? wbm{{p}}_sel_i :
{% endfor %}{SELECT_WIDTH{1'b0}};
assign wbs_stb_o = {% for p in ports %}wbm{{p}}_sel ? wbm{{p}}_stb_i :
{% endfor %}1'b0;
assign wbs_cyc_o = {% for p in ports %}wbm{{p}}_sel ? 1'b1 :
{% endfor %}1'b0;
// arbiter instance
arbiter #(
.PORTS({{n}}),
.ARB_TYPE_ROUND_ROBIN(ARB_TYPE_ROUND_ROBIN),
.ARB_BLOCK(1),
.ARB_BLOCK_ACK(0),
.ARB_LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
)
arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded()
);
endmodule
""")
output_file.write(t.render(
n=ports,
w=select_width,
name=name,
ports=range(ports)
))
print("Done")
if __name__ == "__main__":
main()
最后更新:
2024年9月8日
作者: