(一)总线的实现
- 总线由总线仲裁器,总线主控多路复用器,地址解码器,总线从属多路复用器组成,本文记录总线仲裁器的实现。
原理请看书《CPU自制入门》,书中通俗易懂,图文并茂。
(二)程序源码
1.定义bus.h(书中定义为bus.h)
`ifndef __BUS_HEADER__
`define __BUS_HEADER__
`define BUS_MASTER_CH 4 //总线主控通道数
`define BUS_MASTER_INDEX_W 2 //总线主控索引宽度
`define BusOwnerBus 1:0 //总线所有权状态总线
`define BUS_OWNER_MASTER_0 2'h0 //总线使用权所有者:0号总线主控
`define BUS_OWNER_MASTER_1 2'h1 //总线使用权所有者:1号总线主控
`define BUS_OWNER_MASTER_2 2'h2 //总线使用权所有者:2号总线主控
`define BUS_OWNER_MASTER_3 2'h3 //总线使用权所有者:3号总线主控
`define BUS_SLAVE_CH 8 //总线从属通道数
`define BUS_SLAVE_INDEX_W 3 //总线主控索引宽度
`define BusSlaveLndexBus 2:0 //总线从属索引总线
`define BusSlaveLndexLoc 29:27 //总线从属索引总线
`define BUS_SLAVE_0 0 //0号总线从属
`define BUS_SLAVE_1 1 //1号总线从属
`define BUS_SLAVE_2 2 //2号总线从属
`define BUS_SLAVE_3 3 //3号总线从属
`define BUS_SLAVE_4 4 //4号总线从属
`define BUS_SLAVE_5 5 //5号总线从属
`define BUS_SLAVE_6 6 //6号总线从属
`define BUS_SLAVE_7 7 //7号总线从属
`endif
对比:C语言(#define ) Verilog HDL(`define)
Verilog HDL中的预处理命令和在使用宏时,前面都应该加上(`)
2.定义bus_arbiter.v
`timescale 1ns/1ns //单位时间为1ns,时间精度1ns
`include "stddef.h"
`include "global_config.h"
`include "bus.h" //包含总线头文件
module bus_arbiter(
input wire clk, //时钟
input wire reset, //异步复位
input wire m0_req_, //0号总线主控_请求总线
output reg m0_grnt_, //0号总线主控_赋予总线
input wire m1_req_, //1号总线主控_请求总线
output reg m1_grnt_, //1号总线主控_赋予总线
input wire m2_req_, //2号总线主控_请求总线
output reg m2_grnt_, //2号总线主控_赋予总线
input wire m3_req_, //3号总线主控_请求总线
output reg m3_grnt_ //3号总线主控_赋予总线
);
/****内部信号****/
reg[1:0] owner;
/****赋予总线使用权****/
always @(*) begin
/****赋予总线使用权的初始化****/
m0_grnt_ = `DISABLE_;
m1_grnt_ = `DISABLE_;
m2_grnt_ = `DISABLE_;
m3_grnt_ = `DISABLE_;
/****赋予总线使用权****/
case(owner)
`BUS_OWNER_MASTER_0 : begin
m0_grnt_ = `ENABLE_;
end
`BUS_OWNER_MASTER_1 : begin
m1_grnt_ = `ENABLE_;
end
`BUS_OWNER_MASTER_2 : begin
m2_grnt_ = `ENABLE_;
end
`BUS_OWNER_MASTER_3 : begin
m3_grnt_ = `ENABLE_;
end
endcase
end
/****总线使用权的仲裁****/
always @(posedge clk or `RESET_EDGE reset) begin
if(reset==`RESET_ENABLE)begin
//异步复位
owner <= #1 `BUS_OWNER_MASTER_0;
end else begin
//仲裁
case(owner)
`BUS_OWNER_MASTER_0:begin
if(m0_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_0;
end else if(m1_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_1;
end else if(m2_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_2;
end else if(m3_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_3;
end
end
`BUS_OWNER_MASTER_1:begin
if(m1_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_1;
end else if(m2_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_2;
end else if(m3_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_3;
end else if(m0_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_0;
end
end
`BUS_OWNER_MASTER_2:begin
if(m2_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_2;
end else if(m3_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_3;
end else if(m0_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_0;
end else if(m1_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_1;
end
end
`BUS_OWNER_MASTER_3:begin
if(m3_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_3;
end else if(m0_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_0;
end else if(m1_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_1;
end else if(m2_req_ == `ENABLE_)begin
owner <= #1 `BUS_OWNER_MASTER_2;
end
end
endcase
end
end
endmodule
编译只能检查基本的语法错误,我们采用仿真来验证电路功能,我们开始制作Testbench,由于书中未给出代码,只能自己写。
命名风格主要有两种:bus_arbiter_test.v 和 bus_arbiter_tb.v 。见名知意告诉我:这是bus_arbiter.v的Testbench文件。这里我选择第一种风格。
3.定义bus_arbiter_test.v
`timescale 1ns/1ns //单位时间为1ns,时间精度1ns
`include "stddef.h"
`include "bus_arbiter.h"
`include "global_config.h"
module bus_arbiter_test;
reg clk;
reg reset;
reg m0_req_; //0号总线主控_请求总线
wire m0_grnt_; //0号总线主控_赋予总线
reg m1_req_;
wire m1_grnt_;
reg m2_req_;
wire m2_grnt_;
reg m3_req_;
wire m3_grnt_;
reg owner;
parameter STEP = 100.0000; //10MHZ_100ns
/******生成时钟******/
always #(STEP/2)begin
clk <= ~clk;
end
/******实例化bus_arbiter该模块******/
bus_arbiter bus_arbiter(
.clk (clk),
.reset (reset),
.m0_req_ (m0_req_),
.m1_req_ (m1_req_),
.m2_req_ (m2_req_),
.m3_req_ (m3_req_),
.m0_grnt_ (m0_grnt_),
.m1_grnt_ (m1_grnt_),
.m2_grnt_ (m2_grnt_),
.m3_grnt_ (m3_grnt_)
);
/******测试用例******/
initial begin //初始化信号
#0 begin
clk <= `HIGH;
reset <= `RESET_ENABLE; //在0时刻复位一下
m0_req_ <= `DISABLE_;
m1_req_ <= `DISABLE_;
m2_req_ <= `DISABLE_;
m3_req_ <= `DISABLE_;
// owner <= `BUS_OWNER_MASTER_0; //复位开始后,默认开始为0号主控
end
# (STEP*3/4)
# STEP begin
reset <= `RESET_DISABLE; //解除复位
end
$display( "Test start!"); //测试开始
# STEP begin
m0_req_ <= `ENABLE_; //0号总线主控_请求总线使用权
end
# STEP begin
m0_req_ <= `DISABLE_; //0号总线主控_释放总线使用权
m1_req_ <= `ENABLE_; //1号总线主控_请求总线使用权
end
# STEP begin
m1_req_ <= `DISABLE_; //1号总线主控_释放总线使用权
m2_req_ <= `ENABLE_; //2号总线主控_请求总线使用权
end
# STEP begin
m2_req_ <= `DISABLE_; //2号总线主控_释放总线使用权
m3_req_ <= `ENABLE_; //3号总线主控_请求总线使用权
end
# STEP begin
m3_req_ <= `DISABLE_; //3号总线主控_释放总线使用权
m0_req_ <= `ENABLE_; //0号总线主控_请求总线使用权
end
# STEP begin
$finish; //结束仿真
end
end
/******输出波形文件******/
initial begin
$dumpfile("bus_arbiter.vcd"); //将波形输出到test.vcd文件
$dumpvars(0 , bus_arbiter); //从时刻0开始输出模块bus_arbiter的波形
end
endmodule
可以学一下Icarus Verilog + GTKWave–>http://iverilog.icarus.com/开源轻量型工具,建议是以视频为主来学习。
(三)程序说明
$display( "Test start!"); //测试开始
$display作用相当于C语言中的打印函数printf
$dumpfile("bus_arbiter.vcd"); //将波形输出到test.vcd文件
$dumpvars(0 , bus_arbiter); //从时刻0开始输出模块bus_arbiter的波形
$dumpfile 和 $dumpvars叫做系统任务(现阶段会用就行),可以将仿真时的信号变化输出到波形文件,我们将用GTKWave这个软件来查看波形,支持的波形格式为vcd格式。
(四)仿真开始
打开window的cmd命令窗口,cd切换到该工程文件所在目录(我的:F:\fpga_code\03_bus_arbiter)。
1.仿真文件
iverilog -s bus_arbiter_test -o bus_arbiter_test.out bus_arbiter_test.v bus_arbiter.v
vvp bus_arbiter_test.out
2.GTKWave查看波形
gtkwave bus_arbiter.vcd