一、要求
描述:实现一个深度为8,位宽为4bit的ROM,数据初始化为0,2,4,6,8,10,12,14。可以通过输入地址addr,输出相应的数据data。
输入描述:clk:系统时钟 rst_n:异步复位信号,低电平有效 addr:8bit位宽的无符号数,输入到ROM的地址
输出描述:data:4bit位宽的无符号数,从ROM中读出的数据
二、分析
1、要实现ROM,首先要声明数据的存储空间,例如:[3:0] rom [7:0];
( 每个数据具有多少位,位宽 。 一共需要多少个数据,深度 。 )
2、变量名称rom之前的[3:0]表示每个数据具有多少位,指位宽;
3、变量名称rom之后的[7:0]表示需要多少个数据,指深度,注意这里深度为8,应该是使用[7:0],而不是[2:0]。
4、声明存储变量之后,需要对rom进行初始化,写入数据,然后将输入地址作为rom的索引值,将索引值对应的数据输出。
三、代码
`timescale 1ns/1ns
module rom(
input clk,
input rst_n,
input [2:0]addr, //虽然题目说addr是8条线,但其实3条线已经够用
output [3:0]data
);
reg [3:0] myROM [7:0];
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
myROM[0] <= 0;
myROM[1] <= 2;
myROM[2] <= 4;
myROM[3] <= 6;
myROM[4] <= 8;
myROM[5] <= 10;
myROM[6] <= 12;
myROM[7] <= 14;
end
else begin
myROM[0] <= myROM[0];
myROM[1] <= myROM[1];
myROM[2] <= myROM[2];
myROM[3] <= myROM[3];
myROM[4] <= myROM[4];
myROM[5] <= myROM[5];
myROM[6] <= myROM[6];
myROM[7] <= myROM[7];
end
end
assign data = myROM[addr];
endmodule
四、问题和思考
1、关于 reg [3:0] myROM [7:0]; 和 input [2:0]addr, 的问题
①深度为8,指的是有8个寄存器,所以声明ROM时,必须是[7:0],表示有8个寄存器。 8个寄存器可以存8个数据,地址线只要3根便可以对应这8个数据,所以input [2:0]addr ②input [7:0]addr综合出来的电路,尽管电路输入端显示的是addr[7:0],但是,多路选择器的控制端仍然只用到addr[2:0],显示S[2:0],所以多出来的地址线没有用!
2、data = myROM[addr]; 这条语句应该放进always时序语句还是放在assign组合语句?下面的代码是把它放进always块,会多出一个寄存器。
这两种写法的区别:第①种:如果要求输入的地址addr一变,则输出的数据data立刻变,说明和时序无关,那么便是组合逻辑,则放在always语句外面,用assign语句。2、如果要求输入的地址addr一变,输出的数据data在clk时钟上升沿到时,才进行变化,则放在always时序语句里面。(也就是说,我用地址addr取的数据data我不立马使用,我可以延迟它的输出,让它打一拍再输出)。
要求中给出的波形图中圈出来的部分可知,data是随着addr的变化立刻变化的,即使不在clk的上升沿,即这条语句不受时序控制。所以我们语句应该是,assign data = myROM[addr]; 使用第一种写法。
`timescale 1ns/1ns
module ROM(
input clk,
input rst_n,
input [2:0]addr,
output [3:0]data
);
reg [3:0] myROM [7:0];
reg [3:0] data_r;
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
myROM[0] <= 0;
myROM[1] <= 2;
myROM[2] <= 4;
myROM[3] <= 6;
myROM[4] <= 8;
myROM[5] <= 10;
myROM[6] <= 12;
myROM[7] <= 14;
data_r <= 0;
end
else
data_r <= myROM[addr];
end
assign data = data_r;
endmodule
五、数电知识
1、怎么看ROM电路?可以去B站搜索更详细的讲解视频
先明白三点:1、二极管输出端如果为1,则说明二极管不导通,它的输入端电平看情况, 可能为1也可能为0 2、二极管输出端如果为0,则说明二极管导通,那么它的输入端电平一定为0 3、如果没有二极管,则四条字线W0 W1 W2 W3全导通,且沿线都为高电平。
假设地址位是A1为0 A0为1 。
①接着在地址译码器看字线W0 W1 W2 W3的值。二极管1和二极管2导通,则它们的输入端均为0,沿着字线W2 W3 ,发现二极管6和二极管8的输入端均为0,所以二极管6和二极管8不导通,那么字线W2 W3端口处的电平是二极管1和二极管2的输入端电平,即0 (W2=0 W3=0)
二极管3不导通,二极管7导通,则字线W0的端口处电平为二极管7的输入端电平,即0 (W0=0)
二极管4和二极管5均不导通,则W1端口处的电平为1(悬空)
总结,对于地址译码器部分,只有当一条位线上的二极管全都不导通,则该位线的端口处才是高电平。其他情况的位线端口都是低电平!
②接着在存储矩阵看位线d3 d2 d1 d0的值
二极管只有在输入端为1时,才有可能导通,如果输入端为0,那二极管必然不导通。
由于字线W0 W2 W3均是低电平,则导致与字线W0 W2 W3相接的二极管输入均为0,二极管不导通。
只有W1字线的二极管可能导通,由于它们的输出端均接地,所以W1相接的二极管全导通。
所以 d3=1 d2=0 d1=1 d0=1 1011
总结,对于存储矩阵,只有高电平位线上的二极管导通,由于它们接地,所以全导通!
③最后看输出缓冲器。它由三态门控制,当控制信号有效时,才会把d3d2d1d0输出,才会有D3D2D1D0这些数据。
2、地址译码器、存储矩阵、输出缓冲器
地址译码器其实就是把地址输入的组合拆开,分别由这几种地址组合去选取存储矩阵里的数据。
存储矩阵里面的数据是固化的,也就是它里面的数据出厂就已经确定了。
n条地址线,一共有2的n次方种组合,可以取2的n次方个数据,而数据的值是多少由厂家固定。
六、最后从电路的角度理解设计的要求
reg [3:0] myROM [7:0]; 数据初始化为 0 2 4 6 8 10 12 14
这句话声明了一个ROM,意思是,ROM的存储矩阵的 每个数据有4bit位宽 有8个这样的数据
其中[3:0] 以及 0 2 4 6 8 10 12 14 这些是数据信息,都是厂家固化的在电路的数据信息。
其中[7:0] 表示 存储矩阵的字线和位线都是8条,则对于的地址线是3条,
所以input [2:0]addr (虽然题目说地址线addr是8根,但是实际电路3根就够了)
七、其他一些代码和电路
`timescale 1ns/1ns
module rom(
input clk,
input rst_n,
input [7:0]addr,
output [3:0]data
);
reg [3:0] data_out;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
data_out <= 4'b0000;
else
case (addr)
8'b0000_0000:data_out <= 4'b0000;
8'b0000_0001:data_out <= 4'b0010;
8'b0000_0010:data_out <= 4'b0100;
8'b0000_0011:data_out <= 4'b0110;
8'b0000_0100:data_out <= 4'b1000;
8'b0000_0101:data_out <= 4'b1010;
8'b0000_0110:data_out <= 4'b1100;
8'b0000_0111:data_out <= 4'b1110;
endcase
end
assign data = data_out;
endmodule