从赋值冲突以及设备信息交互对电路带来的问题入手,分析了总线设计的必要性。介绍了总线的工作机理,以及优缺点。并进一步对总线的两种实现方式进行分析。
目录
一、总线的基本概念
1、使用总线的必要性
(1)变量访问思路:“一写多读”
多个并行语句操作同一个变量时,最多只能有一条语句对变量进行写操作(一个信号只能有一个驱动源,否则就会产生冲突);而可以有多条语句对变量进行读操作(类似于一个驱动源驱动多个信号)。
eg1:
always@(a)
begin
c <= a;
end
always@(b)
begin
c <= b;
end
示例中,在两个并行模块对同一变量进行驱动。因为两个模块并行,可能同时发生,因此多个驱动源驱动同一信号,出现冲突,会导致错误;
因此有必要引入总线执行仲裁,避免冲突出现。
(2)总线利于系统设计
如左图所示:引入总线设计之前,ABCDE等设备之间要交互信息,需要两两相连。进一步,如果新添加设备F,需要新引入F到之前所有设备的连接,才能完成系统的扩充更新。可以看出,此种方式连线数目巨大复杂,并且不利于系统的扩充与更新。
如右图所示:引入总线设计之后,ABCDE等设备之间要交互信息,仅需要同bus交互即可。进一步,如果新添加设备F,仅需要新引入F到bus的连接,就能完成系统的扩充更新。可以看出,此种方式设计简单,利于系统的扩充与更新,简化了系统的调试和维修。
2、总线优点
- 简化了系统的结构和硬件设计;
- 便利了系统的扩充和更新;
- 简化了系统的调试和维修;
3、总线缺点
从总线的设计方式可以看出,系统中的各个部件只能分时使用总线。即我们需要通过一定的控制逻辑,在不同时刻为总线分配不同的驱动源。分时复用导致实时性下降;
另一方面,总线设计需要引入额外的控制和筛选逻辑。通过控制和筛选逻辑,保证各设备正确读写总线。
二、总线实现
1、选择器实现
原理与设计:
FPGA芯片中具有丰富的MUX资源,是实现总线的一个常见方法。(体现了硬件描述语言,设计基于硬件的本质)
总线实际就是一个分时复用的过程,即需要控制好某一时间哪一个设备写总线,作为驱动源;某一时间,哪一些设备读总线,作为被驱动的对象。而这一功能可以通过选择器来实现。如图:
控制信号产生部分:根据外围器件的请求情况,产生多路选择器的控制信号;
写总线部分:根据多路选择器的控制信号,选择一个设备作为驱动源;
读总线部分:外围器件根据各自需要,读取总线数据;
2、三态门实现
原理与设计:
三态门资源大多存在于FPGA芯片外部,因此总线位于芯片外部时,常利用三态门实现。(体现了硬件描述语言,设计基于硬件的本质)
总线实际就是一个分时复用的过程,即需要控制好某一时间哪一个设备写总线,作为驱动源;哪一些设备读总线,作为被驱动的对象。而三态门的高阻态相当于断路,可以达到驱动源的选择功能。因此三态门也能用来实现总线。如图:
控制信号产生部分:根据外围器件的请求情况,产生三态门的控制信号;
写总线部分:根据三态门的控制信号的控制信号,选择一个设备作为驱动源(非驱动源设备输出高阻,相当于断路);
读总线部分:外围器件根据各自需要,读取总线数据;
实现代码:
以及三态门为例,进行总线的简单设计。输入端口为abcd四个,各自具有请求信号。认为a具有最高优先级,bcd优先级递减。因此,控制信号部分根据请求信号,生成控制信号。并进一步根据控制信号,选取端口驱动总线。读取部分,读取设备根据自身情况读取总线数据,此处为简化功能,设计为始终读。
`timescale 1ns / 1ps
//
// Company:
// Engineer: CLL guoliang
//
// Create Date: 2020/04/29 11:12:05
// Design Name:
// Module Name: bus_tri
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module bus_tri
#(parameter W = 4)
(
input req_a,req_b,req_c,req_d,
input [W-1:0]a,b,c,d,
output [W-1:0] dout
);
// 声明
wire [W-1:0] bus;
reg [3:0]req;
// 控制信号部分
always@(req_a,req_b,req_c,req_d)
begin
casex({req_a,req_b,req_c,req_d})
4'b1xxx: begin
req = 4'b1000;
end
4'b01xx: begin
req = 4'b0100;
end
4'b001x: begin
req = 4'b0010;
end
4'b0001: begin
req = 4'b0001;
end
default: begin
req = 4'b0000;
end
endcase
end
// 驱动bus
assign bus = (req[3] == 1'b1)?a:{W{1'bZ}};
assign bus = (req[2] == 1'b1)?b:{W{1'bZ}};
assign bus = (req[1] == 1'b1)?c:{W{1'bZ}};
assign bus = (req[0] == 1'b1)?d:{W{1'bZ}};
// 读总线
assign dout = bus;
endmodule
测试文件:
`timescale 1ns / 1ps
//
// Company:
// Engineer: CLL
//
// Create Date: 2020/04/29 11:40:55
// Design Name:
// Module Name: bus_tsb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module bus_tsb(
);
reg req_a,req_b,req_c,req_d;
reg [3:0]a,b,c,d;
wire [3:0]dout;
initial
begin
{req_a,req_b,req_c,req_d}=4'b1000;//选择A驱动
# 200
{req_a,req_b,req_c,req_d}=4'b0010;//选择C驱动
# 200
{req_a,req_b,req_c,req_d}=4'b0100;//选择B驱动
# 50
{req_a,req_b,req_c,req_d}=4'b0001;//选择D驱动
end
initial
begin
a = 4'b0000;
b = 4'b0000;
c = 4'b1111;
d = 4'b1111;
fork
repeat(16)#10 a = a+1;
# 200 repeat(16)#20 c = c-1;
join
end
// 例化语句
bus_tri inst(
.req_a(req_a),
.req_b(req_b),
.req_c(req_c),
.req_d(req_d),
.a(a),
.b(b),
.c(c),
.d(d),
.dout(dout)
);
endmodule
测试文件设计为:
前200us,端口a请求有效,a中数据从0000-1111递增;
200us-400us,端口c请求有效,c中数据从1111-0000递减;
前400us-450us,端口b请求有效,b中数据0000;
450us以后,端口d请求有效,d中数据1111;
仿真结果:
前200us,端口a请求有效,a驱动总线,总线/输出数据为a中数据;与分析一致
200us-400us,端口c请求有效,总线/输出数据为c中1111-0000递减数据;
400us-450us,端口b请求有效,总线/输出数据为b中数据0000;
450us以后,端口d请求有效,总线/输出数据为d中数据1111;与分析一致,可见设计正确。
三、参考文献
《FPGA之道》