8.半导体储存期间
RAM
Xilinx中集成的36KB,18KB 不做解释
//调用块状RAM硬件原语参照表 //
// DATA_WIDTH_A/B | BRAM_SIZE | RAM Depth | ADDRA/B Width | WEA/B Width //
// ===============|===========|===========|===============|=============//
// 19-36 | "36Kb" | 1024 | 10-bit | 4-bit //
// 10-18 | "36Kb" | 2048 | 11-bit | 2-bit //
// 10-18 | "18Kb" | 1024 | 10-bit | 2-bit //
// 5-9 | "36Kb" | 4096 | 12-bit | 1-bit //
// 5-9 | "18Kb" | 2048 | 11-bit | 1-bit //
// 3-4 | "36Kb" | 8192 | 13-bit | 1-bit //
// 3-4 | "18Kb" | 4096 | 12-bit | 1-bit //
// 2 | "36Kb" | 16384 | 14-bit | 1-bit //
// 2 | "18Kb" | 8192 | 13-bit | 1-bit //
// 1 | "36Kb" | 32768 | 15-bit | 1-bit //
// 1 | "18Kb" | 16384 | 14-bit | 1-bit //
//
RTL 编写ram
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/09/18 09:45:06
// Design Name:
// Module Name: csdn_bram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//单口ram
module csdn_bram #(
parameter ADDR_WITHE = 8, //地址位宽8
parameter DATA_WITHE = 8 //位宽8
)
(
input clk,
input en, //读,写使能
input we, //we = 1 为写 ,we = 0为读
input [ADDR_WITHE-1:0]addr,
input [DATA_WITHE-1:0]din,
output [DATA_WITHE-1:0]dout
);
reg[DATA_WITHE-1:0] mem [(1<<ADDR_WITHE)-1:0];
//写控制
always@(posedge clk)
if(en&we)
mem[addr]<=din;//读入
//读控制
reg [DATA_WITHE-1:0]dout_r;
always@(posedge clk)
if(en&~we)//读请求
dout_r<=mem[addr];//读出 (延迟一个时钟输出)
assign dout = dout_r;
endmodule
ROM
1.Xilinx可以导入ROM文件
2.自己写的RTL代码如下
//单口rom
module csdn_bram #(
parameter ADDR_WITHE = 8, //地址位宽8
parameter DATA_WITHE = 10 //位宽10
)
(
input clk,
input en, //读,写使能
input [ADDR_WITHE-1:0]addr,
output [DATA_WITHE-1:0]dout
);
parameter MEM_DEPTH = 1<<ADDR_WITHE;
//定义内存
reg [DATA_WITHE-1:0] mem [MEM_DEPTH-1:0];
//初始化为0
integer i;
initial
begin
for (i=0;i<(1<<MEM_DEPTH;i=i+1)
begin
mem[i] <= 0;
end
end
//读控制
reg [DATA_WITHE-1:0]dout_r;
always@ (posedge clk)
begin
if(en)
begin
dout_r <= mem[addr];
end
end
assign dout = dout_r;
//或者一部组合逻辑读
/*
reg [DATA_WITHE-1:0]dout_r;
always @ (*)
begin
if(en)
dout_r = mem[addr];
end
assign dout = dout_r;
*/
endmodule
双口ram和rom需要根据情况加优先级判断中断
代码差不多这样
1.判断优先级,选择则哪路输出
always@(posedge clk)
begin
casez (in1,in2,in3,in4)
4'b0001: clzres[1:0] <= 2'b11;
4'b001?: clzres[1:0] <= 2'b10;
4'b01??: clzres[1:0] <= 2'b01;
default: clzres[1:0] <= 2'b00;
endcase
end
2.异步读控制
always @ (address or cs or we or oe)
begin : MEM_READ
if (cs && !we && oe) begin
data_out = mem[address];
end
end
3.同步双口ram
//优先级判断
always @ (posedge clk)
begin : MEM_WRITE
if ( cs_0 && we_0 ) begin
mem[address_0] <= data_0;
end
else if (cs_1 && we_1) begin
mem[address_1] <= data_1;
end
end
仿真初始化写法
转自
FPGA 实现 bram (block RAM).
initial begin
cs = 1'b0;
we = 1'b0;
oe = 1'b0;
address = 8'd0;
data_in = 8'h00;
#20
@(negedge clk) begin//read
cs = 1'b1;
oe = 1'b1;
end
for (i = 0; i < 256; i = i + 1) begin
@(negedge clk)
address = i;
end
@(negedge clk) begin//write
we = 1'b1;
oe = 1'b0;
end
for (i = 0; i < 256; i = i + 1) begin
@(negedge clk) begin
address = i;
//此处如何给输入数据?
data_in = data_in + 1;
end
end
@(negedge clk) begin//read
we = 1'b0;
oe = 1'b1;
end
for (i = 0; i < 256; i = i + 1) begin
@(negedge clk)
address = i;
end
@(negedge clk)
cs = 1'b0;
//#100 $finish;
#100 $stop;
其中:@(negedge clk) 在仿真initial中也属于时间向下走,如#20,更加任意控制数据的输入和输出,使用状态机写也类似
10 Verilog硬件描述语言实例
其他略:
状态机:
分类1:按照输出类型是否包含输入的函数
1.Moore型状态机:输出时当前状态的函数 (简单)
2.Mealy:SHUCHU 输出时当前状态和输入的函数
比较:Mealy输入状态变化,则输出立刻变化,而Moore因为输出逻辑不包含输入,所以需要等待到下一个时钟沿来临输出才能变化。
分类2:verilog 写法
分类:一段(时序逻辑),二段(状态变换时序逻辑,输出组合逻辑),三段
状态机的状态转移图,通常也可根据输入和内部条件画出。一般来说,状态机的设计包含下列设计步骤:
• 根据需求和设计原则,确定是 Moore 型还是 Mealy 型状态机;
• 分析状态机的所有状态,对每一状态选择合适的编码方式,进行编码;
• 根据状态转移关系和输出绘出状态转移图;
• 构建合适的状态机结构,对状态机进行硬件描述。
状态机的描述通常有三种方法,称为一段式状态机,二段式状态机和三段式状态机。状态机的描述通常包含以
下四部分:
1)利用参数定义语句 parameter 描述状态机各个状态名称,即状态编码。状态编码通常有很多方法包含自然二
进制编码, One-hot 编码,格雷编码码等;
2)用时序的 always 块描述状态触发器实现状态存储;
3)使用敏感表和 case 语句(也采用 if-else 等价语句)描述状态转换逻辑;
4)描述状态机的输出逻辑。
下面根据状态机的三种方法,来比较各种方法的优劣。
- 一段式状态机是应该避免使用的,该写法仅仅适用于非常简单的状态机设计,不符合组合逻辑与时序逻辑分开的原
则,整个结构代码也不清晰,不利用维护和修改。
但比较直观。 - 大的计数应该写在状态机外面,通过使能或者计数的高位判断进入下一次状态机,这样可以减少case+LUT级联的路径长度。
module detect_2(
input clk_i,
input rst_n_i,
output out_o
);
reg out_r;
//状态声明和状态编码
reg [1:0] Current_state;
reg [1:0] Next_state;
parameter [1:0] S0=2'b00;
parameter [1:0] S1=2'b01;
parameter [1:0] S2=2'b10;
parameter [1:0] S3=2'b11;
//时序逻辑:描述状态转换
always@(posedge clk_i)
begin
if(!rst_n_i)
Current_state<=0;
else
Current_state<=Next_state;
end
//组合逻辑:描述下一状态和输出
always@(*)
begin
case(Current_state)
S0 :
begin
out_r=1'b0;
Next_state= S1;
end
S1 :
begin
out_r=1'b1;
Next_state= S2;
end
S2 :
begin
out_r=1'b0;
Next_state= S3;
end
S3 :
begin
out_r=1'b1;
Next_state=Next_state;
end
endcase
end
assign out_o=out_r;
endmodule
- 两段式状态机采用两个 always 模块实现状态机的功能,其中一个 always 采用同步时序逻辑描述状态转移,另一个
always 采用组合逻辑来判断状态条件转移。两段式状态机是推荐的状态机设计方法。
module detect_3(
input clk_i,
input rst_n_i,
output out_o
);
reg out_r;
//状态声明和状态编码
reg [1:0] Current_state;
reg [1:0] Next_state;
parameter [1:0] S0=2'b00;
parameter [1:0] S1=2'b01;
parameter [1:0] S2=2'b10;
parameter [1:0] S3=2'b11;
//时序逻辑:描述状态转换
always@(posedge clk_i)
begin
if(!rst_n_i)
Current_state<=0;
else
Current_state<=Next_state;
end
//组合逻辑:描述下一状态
always@(*)
begin
case(Current_state)
S0:
Next_state = S1;
S1:
Next_state = S2;
S2:
Next_state = S3;
S3:
Next_state = Next_state;
default :
Next_state = S0;
endcase
end
//输出逻辑:让输出 out,经过寄存器 out_r 锁存后输出,消除毛刺
always@(*)
begin
case(Current_state)
S0,S2:
out_r<=1'b0;
S1,S3:
out_r<=1'b1;
default :
out_r<=out_r;
endcase
end assign out_o=out_r;
三段式状态机在第一个 always 模块采用同步时序逻辑方式描述状态转移,第二个 always 模块采用组合逻辑方
式描述状态转移规律,第三个 always 描述电路的输出。**通常让输出信号经过寄存器缓存之后再输出,消除电路毛刺。**这种状态机也是比较推崇的,主要是由于维护方便,组合逻辑与时序逻辑完全独立。
例题:自动售卖机
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2019/09/18 13:25:44
// Design Name:
// Module Name: Auto_sall
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//三段式状态机
module Auto_sall(
/*
模块:自动售货机电路
描述: 投币口每次只能投入一枚五角或一元硬币。
投入一元五角给个饮料,投入两元(2个一元),在给出饮料的同时找回一枚五角硬币
投币时只能一个一个投
设计分析:抽象七个变量
- clk :时钟输入 in
- rst_n:系统复位信号 in
- half_dollar: 投入五角信号 in
- one_dollat : 投入一元硬币 in
- half_out: 找回五角 out
- dispense: 机器售出一瓶饮料 out
- collect: 提示投币者取走饮料 out
*/
/*
状态分析:
实际情况买一瓶饮料投币情况
- 2个一元
- 1个1元1个5角
- 3个5角
*/
input clk,
input rst_n,
input half_dollar,
input one_dollar,
output half_out,
output dispense,
output collect
);
//定义状态,二进制编码
parameter IDLE = 2'd0,ONE = 2'd1,HALF0 =2'd2,HALF1 = 2'd3;
//经过分析判断,电路为Moore状态机,即输入数据改变后,需要等待时钟到来数据才会变化
reg [1:0] next_state;
reg [1:0] current_state;
//时序逻辑,状态变换
always @ (posedge clk)
begin
if(~rst_n) //同步复位
begin
current_state <=IDLE;
end
else
begin
current_state <= next_state;
end
end
//组合逻辑,描述下一状态
always @ (*)
begin
case(current_state)
IDLE:begin
if(one_dollar&(~half_dollar))
begin
next_state = ONE;
end
else if(~one_dollar&half_dollar)
begin
next_state = HALF0;
end
else
begin
next_state <= IDLE;
end
end
ONE:begin
if((one_dollar&(~half_dollar))|(~one_dollar&half_dollar))
begin
next_state <= IDLE;
end
else
begin
next_state = ONE;
end
end
HALF0:begin
if(one_dollar&(~half_dollar))
begin
next_state <= IDLE;
end
else if(~one_dollar&half_dollar)
begin
next_state = HALF1;
end
else
begin
next_state = HALF0;
end
end
HALF1:begin
if((one_dollar&(~half_dollar))|(~one_dollar&half_dollar))
begin
next_state <= IDLE;
end
else
begin
next_state = HALF1;
end
end
endcase
end
//组合输出逻辑,描述输出(使用锁存器),也可以在加一个寄存器输出消除毛刺
reg half_out_r;
reg dispense_r;
always @ (*)
begin
case(current_state)
IDLE:begin
half_out_r = 0;
dispense_r = 0;
end
ONE:begin
if(one_dollar&(~half_dollar))
begin
half_out_r = 1;
dispense_r = 1;
end
else if(~one_dollar&half_dollar)
begin
half_out_r = 0;
dispense_r = 1;
end
else
begin
half_out_r = 0;
dispense_r = 0;
end
end
HALF0:begin
if(one_dollar&(~half_dollar))
begin
half_out_r = 0;
dispense_r = 1;
end
else if(~one_dollar&half_dollar)
begin
half_out_r = 0;
dispense_r = 0;
end
else
begin
half_out_r = 0;
dispense_r = 0;
end
end
HALF1:begin
if(one_dollar&(~half_dollar))
begin
half_out_r = 1;
dispense_r = 1;
end
else if(~one_dollar&half_dollar)
begin
half_out_r = 0;
dispense_r = 1;
end
else
begin
half_out_r = 0;
dispense_r = 0;
end
end
endcase
end
assign half_out=half_out_r;
assign dispense = dispense_r;
endmodule
也可以对输出打一拍消除毛刺
仿真
module test(
);
reg clk;
reg rst_n;
parameter clk_period=50;
parameter clk_half_period=clk_period/2;
reg half_dollar;
reg one_dollar;
// 初始化
initial
begin
clk=1;
rst_n = 0;
one_dollar=0;
half_dollar = 0;
#200 rst_n = 1;
//
@(posedge clk)
@(posedge clk) //1元
begin
one_dollar =1;
half_dollar =0;
end
@(posedge clk) //0.5元
begin
one_dollar =0;
half_dollar =1;
end
//
@(posedge clk) //1元
begin
one_dollar =1;
half_dollar =0;
end
@(posedge clk) //1元
begin
one_dollar =1;
half_dollar =0;
end
//
@(posedge clk) //0.5元
begin
one_dollar =0;
half_dollar =1;
end
@(posedge clk) //1元
begin
one_dollar =1;
half_dollar =0;
end
//
@(posedge clk) //0.5元
begin
one_dollar =0;
half_dollar =1;
end
@(posedge clk) //0.5元
begin
one_dollar =0;
half_dollar =1;
end
@(posedge clk) //0.5元
begin
one_dollar =0;
half_dollar =1;
end
@(posedge clk) //0元
begin
one_dollar =0;
half_dollar =0;
end
end
always
#clk_half_period clk=~clk;
Auto_sall A1(
.clk(clk),
.rst_n(rst_n),
.half_dollar(half_dollar),
.one_dollar(one_dollar)
);
endmodule
11.脉冲变换电路 略
数模与模数转换器 略
A/D种类:
1.逐次逼近性:类似不断加减砝码(这里是电压),最后确定
2.双积分型:先把模拟变量转换一个中间变量,然后在量化编码。
3.流水线型:速度快:原理跟二分法很类似,只不过残差为2倍的差值,而且起始每一级可能不同,与上一级位置有关