1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
| 结构建模 verilog HDL中的结构建模风格,结构建模风格用以下3种 Gate实例引用语句 UDP实例引用语句 or and xor module 实例应用语句 模块 在Verilog HDL种,一个模块定义了一个基本单元,格式 module module_name(port_list); Declarations_and_Statements endmodule 端口列表(port_list)列出了该模块与外部模块进行通信的端口。 端口 模块的端口可以被声明为输入、输出、双向端口。端口的默认类型为线网类型(即wire类型),也可以明确地把端口声明为线网类型。在声明端口后,可在模块内将输出端口再次声明为reg变量类型。无论是在线网声明还是变量声明中,线网 变量必须与端口声明中指定的位宽一致。 端口声明还可以直接放在端口列表中而不放在模块内部,这样的端口声明风格称为模块端口声明风格 模块端口列表风格(端口声明放在模块内部)。 模块端口声明风格 module ( input wire[3:0] prog_ctr, output wire[1:0]instr_reg, inout wire [15:0]next_addr );
endmodule 端口在端口列表中被声明,那么就不能在模块内部重新声明。端口还可以被进一步限定为寄存器类型(reg)端口 、有符号类型(signed)端口。 module remap_ctrl( input[31:0]rpc_addr, //默认的类型是无符号线网类型 input signed[31:0]rpc_rdata, //有符号线网类型 output reg remap,ready, //无符号寄存器类型变量 output reg signed [31:0]rpc_wdata//rpc_wdata是有符号寄存器类型变量 ); endmodule 所有端口都是用逗号隔开,共用同一个声明的多个端口 参数端口 除了可以在模块内部声明参数外,还可以用端口列表的风格来声明参数。 module module_name #(parameter param1 = value1,param2 = value2,..., parameter param3 = value3) //声明参数 (port_list); ... endmodule 在参数声明中可以选择 指定参数的位宽以及符号。 module sspm #(parameter SIZE = 6,WIDTH = 8, parameter [WIDTH-1:0]HOLD_VALUE = 56, parameter DUMP_FILE = "dump.rpt") (input chip_select,read_write, output [WIDTH-1:0]data); ... endmodule 若每一个实例引用语句中的参数都一样,换言之,参数值不是由实例引用语句来指定的,则参数应该被声明为局部参数。 否则参数既可以在模块内部声明,也可以在参数端口中声明。 模块实例引用: 按照位置排列的连接 按照名称的连接 连接类型:标识符(变量 线网)、位选择、部分选择、上述类型的拼接、表达式(只适用于输入端口) 未连接的端口 实例引用语句中,若端口表达式的位置为空白,就将该端口指定为未连接的端口。 例子 df u0df(.q(qs), .qb(), //输入端口 .cock(ck), .qd() //输出端口 ); //按照名称对应 df u1df(qs, ,ck,,); //按照位置对应 模块未连接输入端的值被设置为z。模块未连接的输出端只是表示该输出端口没有被使用。 不同的端口位宽 在端口和端口表达式之间存在着一种隐含的连续赋值的关系。 因此当端口和端口表达式的位宽不一致时,会进行端口匹配,采用的位宽匹配规则与连续赋值时使用的规则相同。 module child(psa,ppy); input [5:0]psa; output [2:0]ppy; ... endmodule module top; wire [1:2] bdl; //2位 wire [2:6] mpr; //5位 child u6child( .psa(bdl), .ppy(mpr) ); endmodule 模块child的实例引用语句中,存在两个隐含的连续赋值。 psa是输入端口,所以从端口表达式往端口进行赋值 assign psa = bdl; ppy是输出端口,所以从端口往端口表达式进行赋值 assign mpr = ppy; bdl[2]->psa[0],bdl[1]->psa[1]。psa[5] psa[4] psa[3] psa[2] 未连接,因此值为z mpr[6]<-ppy[0],mpr[5]<-ppy[1],mpr[4]<-ppy[2] 模块参数值 一个模块在另一个模块的内部被实例引用时,较高层次的模块能够改变较低层次模块参数的参数值。用以下两种途径可以改变模块实例的参数: 1.使用定义参数语句(defparam语句)修改参数值 2.在模块实例引用中修改参数值 1.定义参数语句 定义参数语句的格式如下: defparam hier_path1 = value1, hier_path2 = value2,...; 在较低层次模块中的参数层次路径明可以使用如下这样一条语句清晰地矛以设置(层次路径名), module top (tpa,tpd,tps,tpc); input tpa,tpd; output tps,tpc; defparam u5ha.XOR_DELAY = 5, //名为u5ha的半加器实例中的参数XOR_DELAY u5ha.AND_DELAY = 2; //名为u5ha的半加器实例中的参数AND_DELAY half_adder u5ha(tpa,tpb,tps,tpc); endmodule module top2(tp2p,tp2q,tp2cin,tp2sum.tp2cont); input tp2p,tp2q,tp2cin; output tp2sum,tp2cout; defparam u8fa.u1ha.XOR_DELAY = 2, //在名为u8fa的全加速器实例中引用的名为u1ha的半加器实例中的参数XOR_DELAY u8fa.u1ha.AND_DELAY = 3, //名为u8fa的全加速器实例中引用的名为u1ha的半加器实例中的参数AND_DELAY u8fa.OR_DELAY = 3; //名为u8fa的全加器实例中的参数OR_DELAY full_adder u8fa(tp2p,tp2q,tp2cin,tp2sum,tp2cout); endmodule 模块实例引用中修改参数值 模块实例的本身就能指定新的参数值,模块实例引用语句可采用以下两种风格来指定实例中的参数值: (1)按照位置来赋对应的参数值(位置关联):第1个值对应于模块中声明的第1个参数,第2个值对应于模块中声明的第2个参数,依此类推 (2)按照名称来赋对应的参数值(名称关联):参数的名称和值都被明确地标出。因此参数地顺序并不重要,从而可以按照任意顺序来传递参数。更重要的是,不是所有的参数值都必须明确的标出 下面功能一样 module top3(tp3a,tp3d,tp3s,tp3c); input tp3a,tp3d; output tp3s,tp3c; half_adder #(5,2) u4ha(tp3a,tp3d,tp3s,tp3c); //第1个值5赋给参数AND_DELAY,该参数是在模块half_adder中声明的第1个参数 //第2个值2赋给参数XOR_DELAY,该参数是在模块half_adder中声明的第2个参数 //这是通过位置来赋值 //下面是通过名称来赋参数值 half_adder #(.AND_DELAY(5),.XOR_DELAY(2)) u12ha(tp3a,tp3d,tp3s,tp3c); endmodule module top4(tp4p,tp4q,tp4cin,tp4sum.tp4cont); input tp4p,tp4q,tp4cin; output tp4sum,tp4cont; defparam u22fa.u1ha.XOR_DELAY = 2, //全加器实例,u22fa中引用半加器实例u1ha中的参数XOR_DELAY u22fa.u1ha.AND_DELAY = 3; //全加器实例,u22fa中引用半加器实例u1ha中的参数AND_DELAY full_adder #(3) u22fa(tp4p,tp4q,tp4cin,tp4sum.tp4cont); //值3是参数OR_DELAY的新值 endmodule 按照位置来赋参数值时,模块实例引用语句中的参数值顺序必须于与参数在被引用的底层模块中被声明的顺序一致。在模块top3中的半加器实例u4ha中,AND_DELAY 设置5 ,XOR_DELAY 设置 2。 模块top3 和top4 的解释说明了用带参数值的模块实例引用语句只能将参数值向下传递一个层次(例如 OR_DELAY),但是用参数定义语句能够修改任意层次的参数值。 注意:在带参数值的模块实例引用语句中,指定参数值位置的标记符与门级实例引用语句中定义延时的位置标记符相似。 外部端口 模块定义中,端口列表举出了模块外部可见的端口 module scram_a(arb,ctrl,mem_blk,byte); input [0:3] arb; input ctrl; input [8:0]mem_blk; output [0:3]byte; ... endmodule arb、ctrl、mem_blk、byte 为这个模块的端口。这些端口同时也是外部端口,即在实例引用中,当采用按照名称 在模块定义的端口列表中,这两种风格可以混合使用,换言之,在模块定义中允许只有部分端口拥有外部端口名称。 如果模块端口是按位置对应关系进行连接的,则模块实例应用语句中不能使用外部端口名称。(端口名称不能与模块实例名相同) 内部端口名称不经可以是标识符,也可以是下面类型的表达式之一: 位选择;部分选择 位选择、部分选择和标识符的拼接项。 下面举例说明: module scram_c( arb[0:2],ctrl, {mem_blk[0],mem_blk[1]},byte[3] ); input [0:3] arb; input ctrl; input [8:0]mem_blk; output [0:3]byte; ... endmodule 在scram_c模块的定义中,端口列表中包括部分选择(arb[0:2])、标识符(ctrl)、拼接项({mem_blk[0],mem_blk[1]})和位选择(byte[3])。在外部端口是位选择、部分选择、拼接项的情况下,不能隐式地指定外部端口的名称。因为若这样定义模块端口的话,在模块实例引用语句中,模块端口必须按位置对应关系才能进行互连, 示例 scram_c u_scram_c( cab[4:6],ram_ctl,mmy[1:0],tcb ); 这个实例引用语句中,端口按照位置对应关系来进行互连;因此,cab[4:6]连接到arb[0:2],ram_ctl连接到ctrl,mmy[1]连接到mem_blk[0],mmy[0]连接到mem_blk[1],tcb连接到byte[3]。arb[3] mem_blk[8:2]没有连接到任何信号,因此这些输入信号将被赋予默认值Z 当内部端口信号与外部连接所需要的表示信号的位宽不完全一致时,若想要使用按名称对应关系连接,(内部端口并不所有信号需要与外部连接时,但是又要按名称对应关系连接,采用方式)则必须为模块内部的端口明确地指定外部端口名称。 如下 module scram_d( .data(arb[0:2]), .control(ctrl), .mem_word(mem_blk[0],mem_blk[1]), .addr(byte[3]) ); input [0:3]arb; input ctrl; input[0:8]mem_blk; output [0:3]byte; ... endmodule 在模块scram_d的实例引用语句中,端口既能够按位置对应关系,也能够按名称对应关系连接,但是不能混合使用。 按名称对应关系连接的实例引用语句。 scram_d u_scram_d( .data(cab[4:6]),.control(ram_ctrl), .mem_word(mmy[1:0]),.addr(tcb) ); 模块也可以只有外部端口而没有相应的内部端口。例如: module scram_e( .data ,.control(ctrl), .mem_word({mem_blk[0],mem_blk[1]}),.addr() ); input ctrl; input [8:0]mem_blk; ... endmodule 模块scram_e有两个外部端口data、addr 没有与模块内部的任何信号相连。 一个内部端口是否能与多个外部端口相连? Verilog HDL 允许一个内部端口与多个外部端口相连。例: module fan_out( .a(ctrl_in),.b(cond_out),.c(cond_out) ); input ctrl_in; output cond_out; assign cond_out = ctrl_in; endmodule 内部端口cond_out与两个外部端口b和c相连。因此在b和c上都将出现cond_out的值。 module ssp_ctrl( .wdata({write_bus[15:12],write_bus[3:0]}) ); input [15:0]write_bus; endmodule module top_ssp_ctrl; reg [7:0] ssp_select; ssp_ctrl u_ssp_ctrl(.wdata(ssp_select)); endmodule 上面描述模块ssp_ctrl的外部端口名称是wdata,而模块top_ssp_ctrl有选择性地把ssp_select的8个位依次连接到write_bus[15:12] 和 write_bus[3:0] 上。 generate(产生,引起) 语句 允许细化时间(Elaboration - time)的选取或者某些语句的重复。这些语句可以包括模块实例引用语句、连续赋值语句、always语句、initial 语句 、门级实例引用语句等。 细化时间是指仿真开始前的一个阶段,此时所有的设计模块已经被链接到一起,并已完成层次引用。 generate 语句用关键字 generate ... endgenerate 来定界 在 generate 语句中可以出现以下 3 种语句。 (1) generate - loop 循环语句 (2) generate - case 分支语句 (3) generate - conditional 条件语句 generate 格式 generate //generate 循环语句 //generate 条件语句 //generate 分支语句 //嵌套的 generate 语句 endgenerate generate 循环语句 (generate 循环语句 主要功能是帮助设计者编写由多次重复所构造的复杂语句。这可借助于基于结构单元的 generatefor 循环语句,在Verilog编译细化阶段,自动地生成由结构单元实例构成的复杂代码.) generate 循环语句被用于(Verilog 编译)细化阶段的语句复制,基本上允许对结构元素编写一个for循环。 例 普通N位的异或门 module nbit_xor (#(parameter SIZE = 16)) ( input [SIZE-1:0] a,b, output [SIZE-1:0] y; ); genvar gv_i; generate for(gv_i = 0;gv_i<SIZE;gv_i = gv_i +1) begin: sblka xor uxor(y[gv_i],a[gv_i],b[gv_i]); end endgenerate endmodule 在细化期间,generate 循环语句将被扩展。对于循环变量的每一个值,for循环语句的实体会被重复一次。 例子喜欢后变成 xor sblka[0].uxor(y[0],a[0],b[0]); xor sblka[1].uxor(y[1],a[1],b[1]); ... 通常, generate 循环语句的格式如下: for(intitial_expression;final_expression;assignment) begin: label statements end 在for循环语句内,循环变量需要被初始化并且在每一次循环时都需要被修改。使用的这种循环变量被称为genvar变量。这种变量必须用genvar 声明语句来声明,并且只能够在generate 循环语句种使用。genvar 变量用于初始化语句和genvar 赋值语句中。 在模块 nbit_xor中,genvar 变量是gv_i 。 初始化时,将其赋值为0,然后每一次循环后加1。 generate 块需要标签, 这个标签用来表示 generate循环的实例名称 。在模块 nbit_xor 中,generate 块的标签是 sblka 。 实例uxor的层次路径是: nbit_xor.sblka[0].uxor nbit_xor.sblka[1].uxor ... 在 generate 语句中还可以有局部声明。例,如果我们在块 sblka 中包括了如下声明: wire phy2; 那么将会出现线网变量phy2的16个实例 ,它们的层次引用名称为: sblka[0].phy2 sblka[1].phy2 ... 在细化期间,for 循环语句被扩展,即对于每一次循环,重复一次 for 语句的实体。 如果在 generate 语句内有多个语句,所有的语句都将被重复。尽管上例中的 generate 语句中只有一个门级实例引用语句,但是 generate 语句还可以包含过程性语句。 下面是一个波纹计数器的示例。 module ripple_counter #(parameter BITS = 8) ( input count_clk,nreset, output[BITS-1:0]q ); genvar gv_a; jk_ff u0jk_ff( .J(1'b1),.K(1'b1),.NRESET(nreset), .CK(cond_out),.Q(q[0]),.NQ()); generate for(gv_a = 1; gv_a < BITS; gv_a = gv_a +1'b1) begin : gblk_a jk_ff u1jk_ff( .J(1'b1),.K(1'b1),.NRESET(nreset), .CK(q[gv_a -1]),.Q(q[gv_a]),.NQ()); end endgenerate endmodule genvar 变量是 gv_a 。在 generate 语句外部实例引用了一个名为 u0jk_ff 的模块实例。 在 generate 语句内部声明了另外的 BITS-1 个实例。 generate - conditional 条件语句 generate 条件语句允许在细化期间对语句进行条件选择。generate 条件语句的常见格式如下: if(condition) statements [else statements] condition 必须是一个静态的条件,既在喜欢期间计算得出(这样一个条件表达式只能有常数和参数组成)。 statements可以是任何能够在模块出现的语句、 例如,always语句、模块实例引用语句和门级实例引用语句。 在细化期间,根据条件的值,选择相应的语句。注意:由于条件的值可能要取决于一个值从上层模块传递过来的参数,因此条件的值可能不能在细化期间被完全计算出来。 例是一个使用generate 循环语句和generate条件语句的移位寄存器。 module shift_register #(parameter BITS = 8) (input shift_in,clk,nreset, output shift_out ); wire [BITS-1:0]tq; genvar gv_k; generate for(gv_k = 0 ; gv_k<BITS ; gv_k = gv_k+1'B1) begin: gblk_x if(gblk_x == BITS-1) begin dflip_flop u0dff( .d(shift_in),.nreset(nreset),.ck(clk), .q(tq[gv_k]) ); end else begin if(gv_k == 0) begin dflip_flop u1dff( .d(tq[gv_k +1]),.nreset(nreset),.ck(clk), .q(shift_out) ); end else begin dflip_flop u2dff( .d(tq[gv_k +1]),.nreset(nreset),.ck(clk), .q(tq[gv_k]) ); end end end endgenerate endmodule 在细化期间,for循环语句被扩展。如果 generate 语句的变量 gv_k 的值为 BITS-1,那么就完成了移位寄存器的第一个部分(串行输入进入了触发器的 d 输入端)。 如果 generate 语句的变量 gv_k 的值为 0 , 那么就完成了移位寄存器的最后一个部分(触发器的输出端输出到 shift_out)。 当 generate 语句的变量 gv_k 取其他的所有值时,则生成了移位寄存器的中间部分(触发器的d端连接到前一个触发器的q端) 在完成细化后(假设BITS的值为8),可以得到: dflip_flop gblk_x[7].u0dff( .d(shift_in),.nreset(nreset),.ck(clk), .q(tq[7]) ); dflip_flop gblk_x[6].u2dff( .d(tq[7]),.nreset(nreset),.ck(clk), .q(tq[6]) ); dflip_flop gblk_x[5].u2dff( .d(tq[6]),.nreset(nreset),.ck(clk), .q(tq[5]) ); ... dflip_flop gblk_x[0].u1dff( .d(tq[1]),.nreset(nreset),.ck(clk), .q(shift_out) ); 用 generate 条件形成得 generate 语句的例子 module adder #(parameter SIZE = 4) (input [SIZE-1 : 0]a,b, output [SIZE-1 : 0]sum, output carr_out ); wire [SIZE-1 : 0] carry; genvar gv_k; generate for(gv_k = 0; gv_k < SIZE; gv_k = gv_k + 1) begin : gen_blk_adder if(gv_k == 0) begin half_adder u_ha( .a(a[gv_k]),.b(b[gv_k]),.sum(sum[gv_k]), .carr_out(carry[gv_k]) ); end else begin full_adder u_fa( .a(a[gv_k]),.b(b[gv_k]), .carry_in(carry[gv_k - 1]),.sum(sum[gv_k]), .carry_out(carry[gv_k]) ); end end endgenerate endmodule generate - case分支语句 generate 分支语句与 generate 条件语句类似,只不过 generate 分支语句是用分支语句来进行条件性选择。 generate 分支语句的常见格式如下: 语句根据参数IMPLEMENTA TION_LEVEL,的值选择相应的加法操作行为。 module my_special_addr #(parameter SIZE = 16, parameter IMPLEMENTA TION_LEVEL = 0) (input[SIZE-1:0] arg1,arg2, output[SIZE-1:0]result ); generate case() 0:assign result = argl + arg2; 1:ripple_adder u_ra(.sum(result),.a(argl),.b(arg2)); 2:cla u_cla(.sum(result),.a(arg1),.b(arg2)); 3:fast_cla u_fcla(.sum(result),.a(argl),.b(arg2)); endcase endgenerate endmodule default分支是可选的。若IMPLEMENTATION_LEVEL的值大于3,则这些分支选项中无论哪一条语句都不会被选中执行。 配置 库的概念,库是存储编译配置、UDP和模块(可以统称位cell(单元))的地方。库可以是逻辑库、符号库,包含许多已编辑好的源代码。 使用库声明语句可以声明带具体内容的库 library library_name "file_name(s)" 这条声明语句能把指定名的文件编译进名为 library_name 的逻辑库中。 库映像文件( library Map File) 是由许多上述库声明语句组成的列表。用include 语句可以在一个映象文件中包含另一个库映像文件。 include library_map_file; 下面举一个库映象文件的例子。 //文件:esoc_library:map library lib_global "gbl/global_blocks.vg"; library lib_rtl "./*.vg"; library lib_work "/home/bond/IP/usb*.v"; include "../../global_lib_definitions"; library lib_gate "./syn/gate/*.vg"; 库映像文件 esoc_library.map 包含了4个库声明语句和1个include 语句 。声明的 4 个库分别是 lib_global 、lib_rtl、lib_work 和 lib_gate。 文件 gbl/global_blocks.vg 被编译进 lib_global 。在当前文件夹中所有扩展名为*.vg 的文件都被编译进库 lib_rtl, 以次类推,在文件名中指定的路径与库映像文件按所在位置有关. 在剖析任何的源代码前,由Verilog HDL的剖析器读入库映像文件。可以拥有多个库映像文件,凡支持Verilog HDL的工具必须提供一种机制去支持读入多个多个库映象文件。 当源文件被编译时,每个源文件中的单元都被保存到库映象文件指定的相应库中去。例如,当文件usb_host.v被编译时,这个文件中所有的单元都被保存在库lib_work中。 现在回到配置这个话题,配置是指确定某实例与库中某个模块的绑定(Binding)关系
|