热度 21
2013-8-31 10:37
1473 次阅读|
2 个评论
本文分为两个部分 第一部分 verilog知识点简要回顾 第二部分 数字时钟设计 第一部分:verilog知识点回顾 1,数值表示方法 verilog支持两种格式的数值表示方法: 指明位宽的数字(sized number)和不指明位宽 的数字(unsized number)。 指明位宽的数字的表示形式为:’ ; 4’ b1001; //4 位宽的二进制数 1001 12’habc; // 12 位的十六进制数 abc 16’d255; //16 位十进制数 如果在数字说明中没有指定基数, 那么默认为十进制数; 如果没有指定位宽, 则默认的 位宽与仿真器、综合器使用的计算机有关(最小为 32 位)。举例如下: 23456; //32 位宽的十进制数 23456 ’habc; // 32 位的十六进制数 abc 2,端口连接规则 3,向量 线网和寄存器类型的变量可以声明为向量(Vector)(位宽大于 1),如果声明中没有指定位 宽,则默认为标量(位宽为 1)。举例如下 wire a; //标量线网变量,默认 reg busA,busB; //声明 busA 和 busB 为 n 位宽寄存器变量, reg busC; //声明 busC 为 n 位宽的寄存器变量 wire busA,busB; //声明 busA 和 busB 为 n 位宽信号, wire busC; //声明 busC 为 n 位宽的信号 向量通过 或者 说明,方括号中左边数总是代表向量的最高有效 位(Most Significant Bit, MSB),在上面的例子中,向量 busA 的最高有效位为第 n-1 位 ,向量busC 的最高有效位为第 0 位。 1、向量域选择 对于上面例子中声明的向量,可以指定它的某一位或若干个相邻位。举例如下 busA //向量 busA 的第 0 位; busA //向量第 3 位;如果写成 busA 非法,高位应该写在范围说明的左侧; busC //向量 busC 的高 2 位; 2、可变的向量域选择 除了用常量指定向量域外, Verilog HDL 还允许指定可变的向量域选择。 这样使设计者可 以通过 for 循环动态地选取向量的各个域,下面是动态域选择的两个专用操作符 : 从起始位 starting_bit 开始递增,位宽为 width; : 从起始位 starting_bit 开始递增,位宽为 width; 4,数组 在 Verilog HDL 中允许声明 reg 以及 wire 类型向量以及标量的数组,对数组的维数没有 限制。 线网数组也可用于连接连接实例的端口, 数组中的每个元素可以作为变量或者向量使 用。 reg port_id //由 8 个 5 位宽的向量组成的数组,数组的每个元素为 5 位宽变量 5 always语句 always@( ) begin ; ; ; end 称为敏感列表, 敏感列表是可选的, 有时也被称为事件控制表达式(event control expression)。 规则 • always 只能赋值寄存器reg integer real time realtime 类型 • 启动仿真时所有always 都开始执行而且在仿真过程中持续执行当到达always 的最后一条 从仿真角度讲, 敏感信号值发生改变, always 块会对其改变做出 响应,即顺序执行 always 块中的所有语句。 always 块如果包含多条语句,则需要将多个语句置于 begin 和 end 之间。 如果只有 1 条语句,则可以省略 begin 和 end。注意:出于代码 可维护性的考虑,建议只有一条语句时也使用 begin 和 end。 举例如下: //电平敏感的敏感列表 always@(a or b or c) //always 块描述的组合逻辑电路的输入为 a,b 和 c。 always@(a , b, c) //另外一种形式的敏感列表, 敏感列表包含多个信号时可以采// 用关键字 or 分割,也可以采用逗号(;)分割; always@* //另外一种形式的敏感列表,表示 always 块中所有赋值表达式 //中的所有变量都是敏感信号。 6,阻塞与非阻塞 过程赋值语句只能出现在 always 块或者 initial 块中,分为两种类型:阻塞赋值(blocking assignment)和非阻塞赋值(non-blocking assignment)。其基本语法如下: = expression1; 阻塞赋值语句 = expression2; 非阻塞赋值语句 阻塞赋值语句中, 赋值表达式首先被计算, 之后将计算结果赋值给左侧变量。 此过程连续执行,在完成赋值前不能执行其后的其它任何语句(该赋值语句的执行“阻塞”其后其它语句的执行),其行为非常类似 C 语言中变量赋值过程。 无论是阻塞赋值还是非阻塞赋值都只能对寄存器类型(reg)的变量赋值 。这里需要强调:虽然过程赋值语句只能对寄存器类型的变量赋值, 并不代表最终的综合结果一定包含寄存器。 非阻塞赋值语句执行时, 首先计算表达式的值, 但并不会立即将表达式的值赋予左侧变量,赋值操作会在 always 块所有语句执行完成之后再将表达式的值赋予左侧变量(赋值过程并不会“阻塞”其后的其它语句的执行)。 对于 Verilog HDL 的初学者,阻塞赋值语句和非阻塞语句的使用非常困难。如果不能理 解二者的区别往往导致最终的电路出现竞争和冒险。 这里给出 2 条经验规则(rules of thumb): 组合逻辑描述中使用阻塞赋值语句; 时序电路的描述中使用非阻塞赋值语句; 7,if else //类型 1:只有 if 子句,不包含 else 分支 if(expression) begin //expression 为真时,执行 true_statement1; true_statement2; … end //类型2 if(expression) begin //expression 为真时,执行 true_statement1; true_statement2; … end else begin //expression 为假时,执行 false_statement1; false_statement2; … end 举例 always @* if (en==1'b0) //等价于 if(~en) y = 4'b0000; else if (a==2'b00) y = 4'b0001; else if (a==2'b01) y = 4'b0010; else if (a==2'b10) y = 4'b0100; else y = 4'b1000; 8,case 语句语法 case (case_expression) alternative1: begin procedural_statement11; procedural_statement12; … end alternative2: begin procedural_statement21; procedural_statement22; … end … alternativen: begin procedural_statementn1; procedural_statementn2; … end default: begin procedural_statement_1; procedural_statement_2; … end endcase 举例 always@* begin case(s) 2'b00: y=a; 2'b01: y=b; 2'b10: y=c; 2'b11: y=d; default: y=8'bxxxxxxxx; endcase end 第二部分:数字时钟设计 设计思路 1,首先我们要让代表秒的led每秒闪烁一次 所以我们要设计一个2hz 的时钟来提供led闪烁 2,其次我们设计三个计数器 周期分别是60 60 24代表秒 分 时 计数满后向相邻计数器进1 3,数码管驱动程序每ms把计数器数据显示到数码管上即可 第二部分数字时钟设的 1,制作2hz时钟驱动led每秒亮一次 verilog语言 : 高亮代码由发芽网提供 //****************************************Copyright (c)*************************** // //file name:myclock.v //作用:产生1HZ时钟 并驱动LED1秒亮一次 // // ---------------------------------------------------- // //by:wenzer //date:2013-08-30 //version:v0.01 //Descriptions: The original version // //********************************************************************************* module myclock ( input sys_clk_50m , input sys_rst_n , output reg LED ); reg div_cnt_ms ; reg div_cnt_500ms ; reg clk_ms ; reg clk_500ms ; // 50M = 20ns , 20ns * 25000 *2 ~= 1ms always @( posedge sys_clk_50m or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) div_cnt_ms = 17'b0 ; else if ( div_cnt_ms = 17 'd25000 ) div_cnt_ms = 17'b0 ; else div_cnt_ms = div_cnt_ms + 17'b1 ; end always @( posedge sys_clk_50m or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) clk_ms = 1'b0 ; else if ( div_cnt_ms == 17'b0 ) clk_ms = ~ clk_ms ; else ; end always @( posedge clk_ms or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) div_cnt_500ms = 17'b0 ; else if ( div_cnt_500ms = 17 'd250 ) div_cnt_500ms = 17'b0 ; else div_cnt_500ms = div_cnt_500ms + 17'b1 ; end always @( posedge clk_ms or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) clk_500ms = 1'b0 ; else if ( div_cnt_500ms == 17 'd250 ) begin clk_500ms = ~ clk_500ms ; end end always @( posedge clk_500ms ) begin LED = ~ LED ; end endmodule 2,实现三个计数器 周期分别为24小时 60分 60秒 60=11 1100 6位 24= 1 1000 5位 verilog语言 : 高亮代码由发芽网提供 // //秒计数器 // always @( posedge clk_sec or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) cnt_sec = 6'b0 ; else if ( cnt_sec = 6 'd60 ) cnt_sec = 6'b0 ; else cnt_sec = cnt_sec + 6'b1 ; end // //分计数器 // always @( posedge clk_sec or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) cnt_min = 6'b0 ; else if ( cnt_min = 6 'd60 ) cnt_min = 6'b0 ; else if ( cnt_sec = 6 'd60 ) cnt_min = cnt_min + 6'b1 ; end // //小时计数器 // always @( posedge clk_sec or negedge sys_rst_n ) begin if ( sys_rst_n == 1'b0 ) cnt_hor = 6 'd12 ; else if ( cnt_min = 6 'd60 ) cnt_hor = cnt_hor + 6'b1 ; else if ( cnt_hor = 6 'd24 ) begin cnt_hor = 6'b0 ; end end 3,实现每秒把计数器数据显示到数码管 可参考此设计思想 目前目测