分类: Verilog
2016-09-23 15:22:42
原文地址:Verilog编写技巧 作者:李怀远
信号写法 1.信号选通: assign write_strobe = write & begintransfer;
2.使能信号是多个其他信号的逻辑与 assign control_reg_en = (address == 3'b001) && write && chipselect;
3.多级控制信号:三组信号,复合信号用粗体标注。 有些信号会在多处被使用,这样定义信号层次清晰。 assign write_strobe = write & begintransfer; assign stop_strobe_0 = (address == 0) && write_strobe; assign global_reset = stop_strobe_0 && writedata[0]; global_reset信号等效于下面: assign global_reset = (address == 0) &&(write & begintransfer )&& writedata[0];
4.表达式也可以给信号赋值 assign counter_is_zero = (internal_counter == 0); dma_mem_read_idle <= ((dma_mem_read_idle == 1) & (go == 0)) | ((dma_mem_read_idle == 1) & (p1_done_read == 1)) ;
5.信号也可以是多个信号的组合。用{}实现 assign counter_load_value = {period_h_register,period_l_register};
6.寄存器的写选通可以通过片选、写信号和寄存器地址信号的逻辑与实现 assign status_wr_strobe = chipselect && ~write_n && (address == 0);
7. 将寄存器某几位作为控制信号定义出来,以便使用 assign reen = control[5]; assign ween = control[6]; assign leen = control[7];
8. 定义一个ram区域 reg [ 31: 0] mem_array [ 15: 0]; 访问:mem_array[wraddress] <= data;
双向数据端口写法 inout [ 7: 0] LCD_data; assign LCD_data = (address[0]) ? 8'bz : writedata; assign readdata = LCD_data;
多路选择: ①assign read_mux_out = ({32 {(address == 0)}} & time_counter_0[31 : 0]) | ({32 {(address == 1)}} & time_counter_0[63 : 32]) | ({32 {(address == 2)}} & event_counter_0) | ({32 {(address == 4)}} & time_counter_1[31 : 0]) | ({32 {(address == 5)}} & time_counter_1[63 : 32]) | ({32 {(address == 6)}} & event_counter_1) |;
②assign control_readdata_temp = (slave_address == 3'b000)? status_register : (slave_address == 3'b001)? read_address_register : (slave_address == 3'b011)? length_register : (slave_address == 3'b110)? control_register : (slave_address == 3'b111)? checksum_register : 0;
从端口基本写 always @(posedge clk or negedge reset_n)//always中赋值的变量为reg型, begin if (reset_n == 0) data_out <= 0; else if (chipselect && ~write_n && (address == 0)) data_out <= writedata[15 : 0]; end assign out_port = data_out;//把reg型变量连续赋值给wire型变量
端口信号 //一个模块与多个其他模块有接口,端口分布时就按与其他模块连接分组,每一小组可以按先输入再输出排序。 module slave ( // these connect to the clock port clk, reset, // these connect to the slave port slave_address, slave_read, slave_readdata, // these connect control_go, control_done, control_fixed_read_address, // this connects to the irq sender port control_irq );
参数化调用 编写子模块时,尽量多用参数定义,使模块使用场合更灵活多变。 一般在模块端口定义后面紧跟着参数定义。 parameter ADDRESSWIDTH = 32; parameter FIFODEPTH = 32; parameter FIFODEPTH_LOG2 = 5; localparam OFFSETWIDTH = LOG2(BYTEENABLEWIDTH); 上层模块在例化子模块时,可以对参数进行修改,在例化模块后用deparam latency_aware_read_master the_latency_aware_read_master ( .clk (clk), .reset (reset), .control_fixed_location (fixed_read_address), // RCON bit .control_read_base (read_address), ); defparam the_latency_aware_read_master.ADDRESSWIDTH = 32; defparam the_latency_aware_read_master.FIFODEPTH = 32; defparam the_latency_aware_read_master.FIFODEPTH_LOG2 = 5;
寄存器写: 现有读选通信号,再在always中将读选通信号作为判断条件 assign period_l_wr_strobe = chipselect && ~write_n && (address == 2); always @(posedge clk or negedge reset_n) begin if (reset_n == 0) period_l_register <= 849; else if (period_l_wr_strobe) period_l_register <= writedata; end
寄存器读: 先通过多路选择器,在将选中寄存器在always中同步赋值 assign read_mux_out = ({16 {(address == 2)}} & period_l_register) | ({16 {(address == 3)}} & period_h_register) | ({16 {(address == 1)}} & control_register) |);
always @(posedge clk or negedge reset_n) begin if (reset_n == 0) readdata <= 0; else if (read) readdata <= read_mux_out; end |
这是一个在设计中常犯的错误列表这些错误常使得你的设计不可靠或速度较慢为了提高你的设计性能和提高速度的可靠性你必须确定你的设计通过所有的这些检查
xilinx设计需要注意的
**不要在内部产生异步的控制信号 例如复位信号或者置位信号
? 内部产生的异步控制信号会产生毛刺
? 作为替代可以产生一个同步的复位/置位信号这个信号的译码要比需要作用的时刻提前一个时钟周期
**不要使用没有相位关系的多个时钟
? 你也许并不总能避免这个条件在这些情况下确定你已使用了适当的同步电路来跨越时钟域
**不要使用没有相位关系的多个时钟
? 再次你也许并不总能避免这个条件相反许多设计都需要这样在这
些情况下确定你已适当地约束了跨越时钟域的路径
**不要使用内部锁存器
?
内部锁存器会混淆时序而且常常会引入另外的时钟信号
?
内部锁存器在透明门打开时可以被看成是组合逻辑但在门被锁存时 可以被看成是同步元件这将会混淆时序分析
?
内部锁存器常常会引入门控时钟门控时钟会产生毛刺使得设计变得不可靠
性能
**逻辑级的时延不要超过时序预算的百分之五十
?
每个路径逻辑级时延可以在逻辑级时序报告或布局后时序报告中找到详细分析了每个路径之后时序分析器将生成每个路径时延的统计量检查一下总共的逻辑级时延超过了你的时序预算的百分之五十吗?
**IOB 寄存器
?
IOB寄存器提供了最快的时钟到输出和输入到时钟的时延
?
首先有一些限制对于输入寄存器在从管脚到寄存器间不能有组合逻
辑存在对于输出寄存器在寄存器和管脚之间也不能有组合逻辑存在对于三态输出在IOB中的所有的寄存器必须使用同一个时钟信号和复位信号而且IOB三态寄存器必须低电平有效才能放到IOB中三态缓冲器低电平有效所以在寄存器和三态缓冲器之间不需要一个反相器
?
你必须使软件能够选用IOB寄存器你可以设置全局实现选项为输入 输出或输入输出选择IOB寄存器缺省值为关(off)。
?
你也可在综合工具或在用户约束文件UCF中设定使得能够使用IOB寄存器句法为: INST
**对于关键的输出选择快速转换速率
?
可以为LVCMOS和LVTTL电平选择转换速率快速的转换速率会降低输 出时延但会增加地弹所以你必须在仔细考虑的基础之上选择快速转换速率
**流水逻辑
?
如果你的设计允许增加延迟对组合逻辑采用流水操作可以提高性能
?
在Xilinx的FPGA中有大量的寄存器对每一个四输入函数发生器有一个对应的寄存器在牺牲延迟的情况下利用这些寄存器来增加数据吞吐量
**为四输入的查找表结构进行代码优化
?
记住每一个查找表可以建立一个四输入的组合逻辑函数如果你需要更大的功能记住实现该功能所需的查找表的数目
**使用Case语句而不是if-then-else语句
?
复杂的if-then-else语句通常会生成优先级译码逻辑这将会增加这些路径上的组合时延
?
用来产生复杂逻辑的Case语句通常会生成不会有太多时延的并行逻辑 对于Verilog用户可以使用编译向导synopsys
parallel_case
**使用一个或多个核生成器块
? 核生成器块针对
Xilinx的结构进行了优化许多块都可以允许用户配置包括大小宽度和流水延迟
?
查看你设计中的关键路径你是否可以在核生成器中产生一个核来提高键路径性能
**使有限状态机FSM保持在层次中的自己所在的那一级
?
为了允许综合工具完全优化你的FSM它必须在它自己的块中优化如果不是这样的话这将使得综合工具将FSM逻辑和它周围的逻辑一起优化
?
FSM不能包括任何的算术逻辑数据通路逻辑或者其它与状态机不相关的组合逻辑
**使用两个进程或always块的有限状态机
?
下一个状态和输出译码逻辑必须放在独立的进程或always块中这将不允许综合工具在输出和下一个状态译码逻辑之间共享资源
**使用一位有效编码有限状态机FSM
?
一位有效编码通常会在富含寄存器的FPGA中提供最高性能的状态机
**为每一个叶级leaf-level块提供寄存输出
?
叶级块是可以推论逻辑的块而结构级(structural-level)的块仅例化较 底层的块这样就建立了层次
?
如果叶级块被锁存输出则可使综合工具保留层次这可使分析这些代码 的静态时序变得比较容易
?
对边界进行寄存可以使得各个块之间有确定的时序关系
**利用有适当管脚定位约束的数据流
?
Xilinx器件中的数据流是在水平方向上的这里部分的原因是进位链是在垂直方向上的另外还有其它的原因三态缓冲线在水平方向上排列块之间也有水平方向上的直接连接
?
为了利用数据流地址和数据管脚必须放在芯片的左侧或右侧同时注意因为进位链是自下而上的所以将最低位放在最下面控制信号放在芯片的上部和下部
**不同的计数器风格
?
二进制计数器是非常慢的如果你的二进制计数器是关键路径可以考虑使用不同的风格的计数器LFSRPre-scalar或Johnson
**设计是层次化的被分成不同的功能块和技术块
?
设计必须被划分成不同的功能块首先是较顶层的功能块然后是较底层的块你也应该包括特定技术的块
?
设计层次化必须使得设计更可读更易调试更易复用
**复制的高扇出网络
?
这可以通过你的综合工具来进行控制然而为了更紧地控制复制你可以选择复制寄存器
**利用四种全局约束来对设计进行全局的约束周期对每个时钟偏置输入偏置输出管脚-到-管脚
?
你也许会有针对多周期路径失败路径和关键路径的其它约束但是你必须总要从指定四个全局约束开始