74138和74148分别是3-8译码器和8-3编码器。启源师兄让我们结合其功能,意思应该就是一个芯片,当信号从三线端输入,则八线端可以作为输出,此时该芯片为译码器;相反,当信号从八线端输入,则三线端可以作为输出,此时该芯片为编码器。如果此分析正确,则首先可以确定的是11个IO口必须为双向口,通过一个引脚来控制该双向口的模式。FPGA中将双向口综合为三态门,通过一个引脚控制其输入或输出状态。
通过这个题目,收获最大的是在modelsim中对双向口的仿真,和普通端口的仿真要求不一样。这个题目是纯组合逻辑,对于刚入门FPGA的新手来说我觉得是个很好的锻炼,包括我。当然用单片机就更容易实现了,启源师兄应该是希望我们熟悉单片机的IO口操作咯,大家有空就做一下,贴上来一起讨论交流!
算了一下,51单片机的机器周期(24M晶振下)是0.5us,实现该题目,首先需要一个判断语句,大致花费时间是5*0.5us=2.5us,一个赋值语句1us,还有几个计算语句,保守估计5us,则一次译码需要的时间大概是8.5us。而FPGA的话,为了防止竞争冒险现象,延时1ns对信号取反,在延时1ns后对信号计算,立即输出,需要的时间不超过5ns。快了很多。不过FPGA的话是纯电路实现,速度也肯定快。
最后,贴上实现代码和测试代码以及测试数据。不足之处还请大家多多指教~嘻嘻
实现代码如下:
(由于modelsim不能写中文注释,我就用我这烂英文加了英文注释,见笑~)
`timescale 1ns/1ns
module combinechip(
A,
Y,
EN,
inout_en
);
input EN,inout_en;
inout [2:0] A;
inout [7:0] Y;
wire [7:0] Ywire;
wire [2:0] Awire;
wire A2bar,A1bar,A0bar;
wire Y7bar,Y6bar,Y5bar,Y4bar,Y3bar,Y2bar,Y1bar,Y0bar;
//get the negated value
assign #1 A0bar=~A[0];
assign #1 A1bar=~A[1];
assign #1 A2bar=~A[2];
assign #1 Y0bar=~Y[0];
assign #1 Y1bar=~Y[1];
assign #1 Y2bar=~Y[2];
assign #1 Y3bar=~Y[3];
assign #1 Y4bar=~Y[4];
assign #1 Y5bar=~Y[5];
assign #1 Y6bar=~Y[6];
assign #1 Y7bar=~Y[7];
//calculate output port Y and save in the Ywire
assign #2 Ywire[0]=~(A2bar&A1bar&A0bar&EN);
assign #2 Ywire[1]=~(A2bar&A1bar&A[0]&EN);
assign #2 Ywire[2]=~(A2bar&A[1]&A0bar&EN);
assign #2 Ywire[3]=~(A2bar&A[1]&A[0]&EN);
assign #2 Ywire[4]=~(A[2]&A1bar&A0bar&EN);
assign #2 Ywire[5]=~(A[2]&A1bar&A[0]&EN);
assign #2 Ywire[6]=~(A[2]&A[1]&A0bar&EN);
assign #2 Ywire[7]=~(A[2]&A[1]&A[0]&EN);
//calculate the output port A,and save in the A wire
assign #2 Awire[0]=(((Y[1]&Y2bar&Y4bar&Y6bar)|(Y[3]&Y4bar&Y6bar)|(Y[5]&Y6bar)|Y[7])&EN);
assign #2 Awire[1]=(((Y[2]&Y4bar&Y5bar)|(Y[3]&Y4bar&Y5bar)|Y[6]|Y[7])&EN);
assign #2 Awire[2]=(Y[4]|Y[5]|Y[6]|Y[7])&EN;
//if (inout_en) then Y is output,A is input.at this time,it is 3-8 decoder;
//else Y is input,A is output,at this time,it is 8-3 decoder;
assign Y=(inout_en==1)?Ywire:8'bz;
assign A=(inout_en==0)?Awire:3'bz;
endmodule
此代码在Quartus II 9.1 的寄存器级描述结果为:
其实就是把74138和74148中的电路整到一块芯片,然后在输出口加个三态门,通过一个引脚控制而已。
下面是modelsim的testbench代码:
//the combinechip module's testbench
module combinechip_test;
//if I want to use always to change the port's value
//the variable must be reg
reg [2:0] DA;
reg [7:0] DY;
//but if I want to test the inout port
//the variable must be wire
//so change the reg variable firstly,and then assign to wire variable
wire [2:0] A;
wire [7:0] Y;
reg EN,inout_en;
//the module is tested
combinechip t_combinechip(A,Y,EN,inout_en);
//generate input signal--------
initial
begin
DA=3'b0;
DY=8'b0;
EN=1;//enble the decoder
inout_en=1;//inout_en=HIGH,Y is output;
#45 inout_en=0;//inout_en=LOW,Y is input;
//change the input port Y per 5ns
#5 DY=8'h01;
#5 DY=8'h02;
#5 DY=8'h04;
#5 DY=8'h08;
#5 DY=8'h10;
#5 DY=8'h20;
#5 DY=8'h40;
#5 DY=8'h80;
#5 $stop;
end
//It is opposite with above.
//if(inout_en) A is output,Y is input
//else A is input,Y is output
assign A=(inout_en==1)?DA:3'bz;
assign Y=(inout_en==0)?DY:8'bz;
//we can find that the regular
//A0 is 0101...
//A1 is 00110011...
//A2 is 00001111...
//so use the regular to generate signal is convenient
always
#5 DA[0]=~DA[0];
always
#10 DA[1]=~DA[1];
always
#20 DA[2]=~DA[2];
//list the information about EN,inout_en,A and Y for us
initial
$monitor("Time=%t:",$time,"EN=%d,inout_en=%d,A=%b,Y=%b",EN,inout_en,A,Y);
endmodule
仿真结果如下:(为了防止竞争冒险现象,所以当信号输入时,第一纳秒是对信号进行取反;第二纳秒才是对信号进行运算,所以应该是第三纳秒的结果才是准确的。即输入信号后,在2ns内的输出结果是不确定的。其中有一点大惑不解的是,当A作为输入时,3ns时刻的值才是正确的,而当Y作为输入时,需要的转换时间仅为2ns,即2ns时刻的值即稳定了。这个想了很久没想明白,先搁着,可能后面做多了会知道的~)
# Time= 0:EN=1,inout_en=1,A=000,Y=xxxxxxxx
# Time= 2:EN=1,inout_en=1,A=000,Y=1111111x
# Time= 3:EN=1,inout_en=1,A=000,Y=11111110
# Time= 5:EN=1,inout_en=1,A=001,Y=11111110
# Time= 7:EN=1,inout_en=1,A=001,Y=11111100
# Time= 8:EN=1,inout_en=1,A=001,Y=11111101
# Time= 10:EN=1,inout_en=1,A=010,Y=11111101
# Time= 12:EN=1,inout_en=1,A=010,Y=11111111
# Time= 13:EN=1,inout_en=1,A=010,Y=11111011
# Time= 15:EN=1,inout_en=1,A=011,Y=11111011
# Time= 17:EN=1,inout_en=1,A=011,Y=11110011
# Time= 18:EN=1,inout_en=1,A=011,Y=11110111
# Time= 20:EN=1,inout_en=1,A=100,Y=11110111
# Time= 22:EN=1,inout_en=1,A=100,Y=11111111
# Time= 23:EN=1,inout_en=1,A=100,Y=11101111
# Time= 25:EN=1,inout_en=1,A=101,Y=11101111
# Time= 27:EN=1,inout_en=1,A=101,Y=11001111
# Time= 28:EN=1,inout_en=1,A=101,Y=11011111
# Time= 30:EN=1,inout_en=1,A=110,Y=11011111
# Time= 32:EN=1,inout_en=1,A=110,Y=11111111
# Time= 33:EN=1,inout_en=1,A=110,Y=10111111
# Time= 35:EN=1,inout_en=1,A=111,Y=10111111
# Time= 37:EN=1,inout_en=1,A=111,Y=00111111
# Time= 38:EN=1,inout_en=1,A=111,Y=01111111
# Time= 40:EN=1,inout_en=1,A=000,Y=01111111
# Time= 42:EN=1,inout_en=1,A=000,Y=11111111
# Time= 43:EN=1,inout_en=1,A=000,Y=11111110
# Time= 45:EN=1,inout_en=0,A=111,Y=00000000
# Time= 47:EN=1,inout_en=0,A=000,Y=00000000
# Time= 50:EN=1,inout_en=0,A=000,Y=00000001
# Time= 55:EN=1,inout_en=0,A=000,Y=00000010
# Time= 57:EN=1,inout_en=0,A=001,Y=00000010
# Time= 60:EN=1,inout_en=0,A=001,Y=00000100
# Time= 62:EN=1,inout_en=0,A=010,Y=00000100
# Time= 65:EN=1,inout_en=0,A=010,Y=00001000
# Time= 67:EN=1,inout_en=0,A=011,Y=00001000
# Time= 70:EN=1,inout_en=0,A=011,Y=00010000
# Time= 72:EN=1,inout_en=0,A=100,Y=00010000
# Time= 75:EN=1,inout_en=0,A=100,Y=00100000
# Time= 77:EN=1,inout_en=0,A=101,Y=00100000
# Time= 80:EN=1,inout_en=0,A=101,Y=01000000
# Time= 82:EN=1,inout_en=0,A=110,Y=01000000
# Time= 85:EN=1,inout_en=0,A=110,Y=10000000
# Time= 87:EN=1,inout_en=0,A=111,Y=10000000
用户1615925 2012-4-7 10:47
用户284347 2011-7-25 20:28