|
对于CPLD/FPGA初学者而言,如何实现双向信号往往是个难题。duoduo当年初接触CPLD/FPGA的时候也为这个问题头疼过。让我们透过下面这个简单的例子看看CPLD/FPGA设计中如何实现双向信号。
假设HOST通过CPLD/FPGA读写外设(比如MCU通过CPLD/FPGA读写一块memory的情况),为了简化问题的说明,假设HOST端只有写信号hwr_,读信号hrd_和双向8位数据总线hd;外设端相应有写信号swr_,读信号srd_和双向8位数据总线sd。CPLD/FPGA对所有信号只作一个通路的作用,即对HOST和外设来讲,它们都认为自己是直接连接到了对方,而不知道CPLD/FPGA的存在(显然实际电路中这样做可能并没有什么用处,但是这个例子只是用来说明双向信号的用法)。
下图中表示了信号流向,虚线部分表示在CPLD/FPGA中通过的信号流。可以看到,hwr_和swr_,hrd_和srd_都只是单向信号,Hd和Sd则是双向的总线。

单向信号很简单就不多说了,看看双向信号如何实现。首先从CPLD/FPGA的IO结构上分析如何实现双向。尽管IC在IO Pad结构上可能很复杂,但是从实现双向功能这个目的来看,我们可以把IO
Pad结构简化成下面的图示:

图中A_OUT_OE,A_OUT,A_IN均为CPLD/FPGA内部信号,A则是CPLD/FPGA的引脚信号名。显然,A_IN始终反映了A的实际状态,即引脚输入,A_OUT受A_OUT_OE控制,当A_OUT_OE为高的时候A_OUT可以通过三态缓冲器输出到引脚上,即引脚输出。因此,在CPLD/FPGA内部定义A_IN(实际上A_IN就是A),A_OUT,A_OUT_OE三个信号用于内部逻辑,并合适地产生A_OUT_OE信号,就可以实现双向引脚。
根据这个实现双向IO的原理,对上面的HOST读写外设的例子,我们可以这样来描述:
module
bidir(
hwr_,
hrd_,
hd,
swr_,
srd_,
sd,
//dumy
);
input
hwr_;
input
hrd_;
inout
[7:0] hd;
output
swr_;
output
srd_;
inout
[7:0] sd;
//input
dumy;
wire
hd_out_oe;
reg
[7:0] hd_out;
wire
sd_out_oe;
reg
[7:0] sd_out;
always
@(hwr_)
begin
if (!hwr_) sd_out = hd;
end
always
@(hrd_)
begin
if (!hrd_) hd_out = sd;
end
assign
swr_ = hwr_;
assign
srd_ = hrd_;
//assign
sd_out_oe = !hwr_ & dumy;
assign
sd_out_oe = !hwr_;
assign
hd_out_oe = !hrd_;
assign
hd = hd_out_oe ? hd_out : 8'hzz;
assign
sd = sd_out_oe ? sd_out : 8'hzz;
endmodule
这段描述使用的是VerilogHDL,其中有个被注释掉的信号dumy,暂时先不解释。这个描述是可综合的,我们使用Altera的MaxPlusII来仿真一下:
嗯,看起来不错,hwr_有效的时候hd正确传送到了sd,实现了hd输入,sd输出;hrd有效的时候sd正确传送到了hd,实现了sd输入,hd输出,这样hd、sd两个双向8位总线都正确实现了。
且慢,有没有不太好的地方?上面描述中hwr_直接作为了sd输出的使能控制信号sd_out_oe,由于swr_直接来自hwr_,因此swr_上沿到来后sd输出被立即关闭,许多外设器件要求有一定的数据写保持时间,而现在sd的写保持时间却为0。怎样克服这个缺点呢?把前面关于dumy信号的代码去掉注释符号”//”,把assign
sd_out_oe = !hwr_;这句注释掉,再编译仿真一下看看:

哈哈,这下sd的输出保持时间够长了吧?dumy是用户定义的一个CPLD/FPGA外部引脚,按照上面的描述,在实际电路中将其固定接高,由于需要dumy信号和hwr取反然后通过与逻辑再形成sd_out_oe,带有门延迟,因此与最初的描述方法相比,sd输出关闭被延迟了。使用dumy信号这种方法需要额外占用CPLD/FPGA的一个引脚,也可以采取其他方式实现这个延迟功能。而Hd不需要这样特殊处理,为什么?呵呵,读者自己想想看?
还有一个看起来不太好的地方:HOST的hrd_有效而外设尚未将数据准备好的时候,hd总线上有一段时间的不定态,不过这不影响HOST的读功能,一般情况下不需要特殊处理,如果不希望出现这个现象,可以通过sd上拉、时钟同步处理等手段将其消除。
方才上面说过,这个例子只是用来说明双向信号的实现方法,例子本身可能没有什么实际用处,嘿嘿,如果把
assign
hd = hd_out_oe ? hd_out : 8'hzz;
assign
sd = sd_out_oe ? sd_out : 8'hzz;
改写为:
assign hd = hd_out_oe ? {hd_out[3:0],hd_out[7:4]} : 8'hzz;
assign sd = sd_out_oe ? {sd_out[3:0],sd_out[7:4]} : 8'hzz;
发生什么现象了?对,输入输出总线上高低半字节互换了!这就是曾经用过的最简单的一种硬件加密方法,有用了吧?
写文章好累呀,简直比用VerilogHDL写代码还难:)
|