Verilog RTL 代码设计新手上路


需要培养的能力

对于使用硬件描述语言(HDL)来设计集成电路逻辑而言,一个有经验的集成电路逻辑设计人员会拥有以下的能力来完成设计。

本教程的用途

事先规划 RTL 结构

目前成熟的EDA工具都是将RTL(寄存器传输级)层次的硬件描述语言转换为实际的电路。所谓RTL的含义就是D触发器之间穿插着组合逻辑。对于组合逻辑,我们只需描述它输入和输出的关系表达式(用if-else和case语句)不必深究到底用怎样的逻辑门来实现,而对于D触发器,则是一定要做到心中有数,就是哪些变量会生成D触发器。

边沿捕获电路的RTL结构 EDA工具提取出的边沿捕获电路RTL结构

举例来说,一个在CLK时钟驱动下,对输入信号IN进行上跳沿捕获的电路,其电路RTL结构如左图所示,它的Verilog代码如下所示,另外,EDA工具进行编译之后,会提取出RTL结构如右图所示,我们可以观察EDA工具解析出的RTL是否和我们预想的一致,这是一种重要的验证手段。对于Quartus工具,Tools -> Netlist Viewer -> RTL Viewer


// module  top, 边沿捕获器代码,
module top(
  CLK      ,   // input clock
  IN       ,   // input  
  OUT      );  // output
input CLK; input IN; output OUT;
reg  d1R, d2R; // 电路中的D触发器输出端
reg  OUT;      // 组合逻辑输出信号,作为输出端口
// 生成移位寄存的D触发器,我们要明确,下面代码的写法会生成d1R和d2R两个D触发器。
// 并且d1R和d2R级联构成IN信号的移位寄存器。这是EDA工具不会篡改的事实。
always @ (posedge CLK) begin
  d1R <= IN  ;
  d2R <= d1R ;
end
// 判断上跳沿的组合逻辑,我们要明确,下面的代码是一个以d1R、d2R信号作为输入,
// 以OUT信号作为输出的组合逻辑,并且代码中指出了OUT信号与d1R、d2R信号的逻辑
// 关系(好比是真值表),但是究竟用怎样的逻辑门实现(好比是卡诺图的画圈)
// 那是EDA工具要关心的事情。
always @ (d1R or d2R) begin
  if((d2R == 0)&&(d1R == 1)) // 新值为1,旧值为0,跳变发生
    OUT = 1'b1;
  else
    OUT = 1'b0;
end
endmodule
// endmodule top

实例化电路模块并互联

代码

// 顶层模块 
module top(
  IN     ,
  OUT0   , 
  OUT1   );
input [4-1:0] IN ;
output[4-1:0] OUT0;
output[4-1:0] OUT1;
wire [4-1:0] W_add0_out;
wire [4-1:0] W_add1_out;

// 第1次实例化, 加法器子模块 
add U0_add(
  .IN0   (IN        ),  
  .IN1   (4'h1      ),
  .OUT   (W_add0_out));
// 第2次实例化, 加法器子模块 
add U1_add(
  .IN0   (IN        ),
  .IN1   (4'h2      ),
  .OUT   (W_add1_out));

// 端口接线互联
assign OUT0 = W_add0_out;
assign OUT1 = W_add1_out;
endmodule

// 加法器子模块 
module add(
  IN0   ,
  IN1   ,
  OUT   );
input [4-1:0] IN0;
input [4-1:0] IN1;
output[4-1:0] OUT;
reg   [4-1:0] OUT;
always  @ (IN0 or IN1) begin
  OUT = IN0 + IN1;
end
endmodule

RTL 视图


子模块的实例化和互联

多路选择器

电路描述

MUX21 符号 MUX21 时序图

代码

// module  top, 选择器(mux)的代码,
module top(
  IN0       ,   // input 1
  IN1       ,   // input 2
  SEL       ,   // select 
  OUT       );  // out data
parameter WL = 16;      // 输入输出数据信号位宽
input [WL-1:0] IN0, IN1;// 选择器的两个输入数据信号
input SEL;              // 通道选通的控制信号
output[WL-1:0] OUT;     // 选择器的输入数据信号

reg   [WL-1:0] OUT;
// 生成组合逻辑的代码
always @ (IN0 or IN1 or SEL) begin
  if(SEL) // SEL为1 选择输入1
    OUT = IN1;
  else    // SEL为0 选择输入0
    OUT = IN0;
end
endmodule
// endmodule top

学生实验

做一个4选1的mux,并且进行波形仿真 和2选1的mux对比,观察资源消耗的变化

交叉开关

电路描述


2X2交叉开关 符号

代码

// module  top, a 2x2 crossbar switch circuit

module top(
  IN0       ,   // input 1
  IN1       ,   // input 2
  SEL0      ,   // select the output0 source 
  SEL1      ,   // select the output1 source 
  OUT0      ,   // output data 0
  OUT1      );  // output data 1
parameter WL = 16;
input [WL-1:0] IN0, IN1;
input SEL0, SEL1;
output[WL-1:0] OUT0, OUT1;

reg   [WL-1:0] OUT0, OUT1;
// get the OUT0
always @ (IN0 or IN1 or SEL0) begin
  if(SEL0)
    OUT0 = IN1;
  else
    OUT0 = IN0;
end
// get the OUT1
always @ (IN0 or IN1 or SEL1) begin
  if(SEL1)
    OUT1 = IN1;
  else
    OUT1 = IN0;
end
endmodule
// endmodule top

学生实验

编写一个4X4路交叉开关的RTL,然后编译,看RTL View 比较2x2与4x4之间消耗资源的区别。通过对比资源,你有什么结论? 返回顶部

优先编码器

电路描述

波形

4输入优先编码器时序图

代码


// module top, 4 input priority encoder with zero input check
module top(
  IN        ,   // input  
  OUT       );  // output 
input [3:0] IN;
output[2:0] OUT;

reg   [2:0] OUT;
// get the OUT
always @ (IN) begin
   if(IN[3])       // 第一优先
     OUT = 3'b011;
   else if(IN[2])  // 第二优先
     OUT = 3'b010;
   else if(IN[1])  // 第三优先
     OUT = 3'b001;
   else if(IN[0])  // 第四优先
     OUT = 3'b000;
   else            // 什么都没有检测到
     OUT = 3'b111; // 输出值可自定义,不和上面的输出值混淆即可
end
endmodule

学生实验

多路译码器

数字电路课本中的3-8译码器就是多路译码器中的一种。

电路描述

代码


// module top, 4 input priority encoder with zero input check
module top(
  IN        ,   // input  
  OUT       );  // output 

input [2:0] IN;
output[7:0] OUT;

reg   [7:0] OUT;
// get the OUT
always @ (IN) begin
  case(IN)
    3'b000: OUT = 8'b0000_0001;
    3'b001: OUT = 8'b0000_0010;
    3'b010: OUT = 8'b0000_0100;
    3'b011: OUT = 8'b0000_1000;
    3'b100: OUT = 8'b0001_0000;
    3'b101: OUT = 8'b0010_0000;
    3'b110: OUT = 8'b0100_0000;
    3'b111: OUT = 8'b1000_0000;
    //  full case 不需要写default,否则一定要有default
  endcase
end
endmodule

学生实验

加法器

加法器是很常用的电路,主要常用的形式包括

无符号加法器

输入和输出数据都是无符号的整数,常用于计数器累加和计算地址序号

波形

无符号加法器时序图

代码


module top(
  IN1   ,
  IN2   ,
  OUT   );
input[3:0] IN1, IN2;
output[4:0] OUT;
reg[4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

要点

学生实验

补码加法器

输入和输出数据都是2补码形式的有符号数,常用于数字信号处理电路

代码


module top(
  IN1   ,
  IN2   ,
  OUT   );
input signed [3:0] IN1, IN2;
output signed [4:0] OUT;
reg signed [4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

波形

补码加法器时序图

要点

从组合逻辑门的层面而言,补码加法器和无符号加法器的电路逻辑是有较大区别的,是由于EDA工具检测到了"signed"关键字,所以才生成了补码加法器的电路逻辑。 注意补码加法器的输入数据和对应结果在时间上同样是有延迟的,这是组合逻辑的延迟。(请估算一下延迟长度) 波形图上的数据是带负号的,这是通过选择信号-属性-Radix为 有符号的十进制 来观察的结果 请思考,对于同样的二进制比特数据,我们可以用不同的Radix观察它,这说明了什么? 请思考,如果输出的位数和输出的位数相同,那么加法结果在什么情况下是正确的,什么情况下是错误的?

学生实验

把加法器的输出信号改成4比特位宽,编译,波形仿真。观察输出结果,观察输出结果在什么时候是正确的?。 把加法器的输入信号改成8比特位宽,编译,波形仿真。观察加法器的输出延迟,和4比特输入位宽的情况对比,你有什么结论,为什么?

带流水线的加法器

要点

输入和输出都添加了流水线D触发器的无符号加法器,其RTL结构如图所示,点击图片可以放大观察

流水线加法器RTL结构图

代码


module top(
  IN1   ,
  IN2   ,
  CLK   ,
  OUT   );
input  [3:0] IN1, IN2;
input CLK;
output  [4:0] OUT;
reg [3:0] in1_d1R, in2_d1R;
reg  [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
  in1_d1R <= IN1;
  in2_d1R <= IN2;
  OUT     <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
  adder_out = in1_d1R + in2_d1R;
end
endmodule 

波形

流水线无符号加法器时序图

目测一下,和不带流水线的加法器相比,毛刺的时间长度有变化么,为什么? 观察,输入数据和其对应的结果在一个时钟周期么?

学生实验

不改变流水线的级数,把加法器的输入信号改成8比特位宽,编译,波形仿真,和不带流水线的情况对比一下,你有什么结论? 在8比特输入位宽的情况下,在输入上再添加一级流水线,观察编译和仿真的结果,你有什么结论?

乘法器

写在其它内容之前,先要强调的是,乘法器是一种奢侈品会消耗大量的组合电路逻辑资源,一定要慎重使用。尤其是对于芯片内部没有硬件乘法器的FPGA芯片,需要更加慎重。 为什么会消耗如此多的资源呢,请回想一下我们在草稿纸上手工计算多位数乘法的过程,我们需要做很多次的加法,用电路计算乘法也是类似的。

乘法器的代码和加法器类似,有无符号,有符号的乘法器,另外还可以添加流水线。

代码


////////////////////  无符号的乘法器  /////////////////////////
module top(
  IN1   ,
  IN2   ,
  OUT   );
 input [3:0] IN1, IN2;
 output [7:0] OUT;
 reg [7:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 * IN2;
end
endmodule 
////////////////////  有符号的2补码乘法器  /////////////////////////
module top(
  IN1   ,
  IN2   ,
  OUT   );
 input signed[3:0] IN1, IN2;
 output signed [7:0] OUT;
 reg signed[7:0] OUT;
 always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 * IN2;
end
endmodule 

波形

下图是一个2输入4比特,1输出8比特的有符号乘法器的波形图,可以看到其中的组合逻辑延迟和毛刺时间是如此之多
补码乘法器时序图

要点

学生实验

计数器

计数器是最为常用的时序电路之一,计数器在数字电路里面的作用,就如C程序中的for循环一样

最简单的计数器只有一个CLK信号和一个计数值Q信号,在实际应用中的计数器可能会出现较多的变化,例如下图所示的各种端口,完成不同的逻辑功能,列举如下:

带有多种控制信号的计数器RTL图

代码


//////////////////// 计数器代码  /////////////////////////

module top(
  RST   , // 异步复位, 高有效
  CLK   , // 时钟,上升沿有效
  EN    , // 输入的计数使能,高有效
  CLR   , // 输入的清零信号,高有效
  LOAD  , // 输入的数据加载使能信号,高有效
  DATA  , // 输入的加载数据信号
  CNTVAL, // 输出的计数值信号
  OV    );// 计数溢出信号,计数值为最大值时该信号为1

input RST   , CLK   , EN    , CLR   , LOAD  ;
input [3:0] DATA ;
output [3:0] CNTVAL;
output OV;   

reg [3:0] CNTVAL, cnt_next;
reg OV;
// 电路编译参数,最大计数值
parameter CNT_MAX_VAL = 9;

// 组合逻辑,生成cnt_next
// 计数使能最优先,清零第二优先,加载第三优先
always @(EN or CLR or LOAD or DATA or CNTVAL) begin
  if(EN) begin    // 使能有效
    if(CLR) begin // 清零有效
      cnt_next = 0;
    end
    else begin  // 清零无效
      if(LOAD) begin // 加载有效
        cnt_next = DATA;
      end
      else begin     // 加载无效,正常计数
        // 使能有效,清零和加载都无效,根据当前计数值计算下一值
        if(CNTVAL < CNT_MAX_VAL) begin // 未计数到最大值, 下一值加1
          cnt_next = CNTVAL + 1'b1;
        end
        else begin // 计数到最大值,下一计数值为0
          cnt_next = 0;
        end
      end // else LOAD
    end  // else CLR
  end // if EN
  else begin  // 使能无效,计数值保持不动
    cnt_next = CNTVAL;
  end // else EN
end

// 时序逻辑 更新下一时钟周期的计数值
// CNTVAL 会被编译为D触发器
always @ (posedge CLK or posedge RST) begin
  if(RST) 
    CNTVAL <= 0;
  else
    CNTVAL <= cnt_next;
end

// 组合逻辑,生成OV
always @ (CNTVAL) begin
  if(CNTVAL == CNT_MAX_VAL) 
    OV = 1;
  else
    OV = 0;
end

endmodule

波形

下图是计数器代码的波形仿真时序图,从图中的计数值CNTVAL的改变可以看出信号的优先关系

带有多种控制信号的计数器仿真波形图

学生实验

请完成以下设计实验,编译电路并且进行波形仿真。

状态机

有限状态机(Finite State Machine)同样是数字电路设计中非常常用的模块,其在EDA设计中的地位等同于C语言中的If-else语句。

设计要点

参考电路

状态机 的 状态转移图和RTL结构
当前状态 输入 次态
ST_0_CENT CENT1IN==0 ST_0_CENT
ST_0_CENT CENT1IN==1 ST_1_CENT
ST_1_CENT CENT1IN==0 ST_1_CENT
ST_1_CENT CENT1IN==1 ST_2_CENT
ST_2_CENT CENT1IN==0 ST_2_CENT
ST_2_CENT CENT1IN==1 ST_3_CENT
ST_3_CENT Donot care ST_0_CENT
当前状态 输出 TINOUT
ST_0_CENT 0
ST_1_CENT 0
ST_2_CENT 0
ST_3_CENT 1

代码


////////////////////  三段式状态机代码  /////////////////////////
module test_rtl(
  CLK       ,   // clock
  RST       ,   // reset
  CENT1IN   ,   // input 1 cent coin
  TINOUT    );  // output 1 tin cola

input  CLK       ; 
input  RST       ; 
input  CENT1IN   ; 
output TINOUT    ;

parameter ST_0_CENT = 0;
parameter ST_1_CENT = 1;
parameter ST_2_CENT = 2;
parameter ST_3_CENT = 3;

reg [2-1:0]stateR       ;
reg [2-1:0]next_state   ;
reg        TINOUT       ;

// calc next state
always @ (CENT1IN or stateR) begin
  case (stateR)
    ST_0_CENT :begin if(CENT1IN) next_state = ST_1_CENT ; else next_state = ST_0_CENT; end
    ST_1_CENT :begin if(CENT1IN) next_state = ST_2_CENT ; else next_state = ST_1_CENT; end
    ST_2_CENT :begin if(CENT1IN) next_state = ST_3_CENT ; else next_state = ST_2_CENT; end
    ST_3_CENT :begin next_state = ST_0_CENT; end
  endcase
end

// calc output
always @ (stateR) begin
  if(stateR == ST_3_CENT) 
    TINOUT = 1'b1;
  else 
    TINOUT = 1'b0;
end

// state DFF
always @ (posedge CLK or posedge RST)begin
  if(RST)
    stateR <= ST_0_CENT;
  else
    stateR <= next_state;
end

endmodule

对于代码编写的规范的状态机,EDA工具可以检测出来,例如Quartus中,编译代码之后,可以在Tools-Netlist Viewers-State Machine Viewer 里面看到你写的状态机的状态转移图和表达式。这对你调试电路非常有帮助。

Quartus提取的状态机视图

学生实验

设计一个用于识别2进制序列“1011”的状态机

移位寄存器

电路描述

两种移位寄存器RTL结构图

代码


//////////////////// 串入并出移位寄存器  /////////////////////////
module top(
  RST   ,   // 异步复位, 高有效
  CLK   ,   // 时钟,上升沿有效
  EN    ,   // 输入数据串行移位使能
  IN    ,   // 输入串行数据
  OUT   );  // 并行输出数据

input RST, CLK, EN;
input IN;
output[3:0] OUT;
reg [3:0] shift_R;

assign OUT[3:0] = shift_R[3:0];
// 时序逻辑 根据输入使能进行串行移位
// shift_R 会被编译为D触发器
always @ (posedge CLK or posedge RST) begin
  if(RST) 
    shift_R[3:0] <= 0;
  else
    if(EN) begin // 串行移位的使能有效
      shift_R[3:1] <= shift_R[2:0];
      shift_R[0]   <= IN;
    end
    else begin // 使能无效保持不动
      shift_R[3:0] <= shift_R[3:0];
    end
end // always
endmodule

波形

串行输入并行输出移位寄存器波形

学生实验

设计一个如本节“电路描述”部分的“带加载使能和移位使能的并入串出”的移位寄存器,电路的RTL结构图如“电路描述”部分的RTL结构图所示。

双口RAM

关于提供的参考代码

地址宽度,数据宽度均参数化的双口RAM,另外带有RAM内容初始化的仿真功能(注意这只是一个仿真用的功能)

知识点介绍

使用了以下的编译命令(注意这是一种编译命令!虽然它们是写在注释里面的,因为Verilog语言本身是不包含编译控制的语法的,这种写法不推荐广泛使用,只有必要时采用,因为严格来说这不是一种规范的做法)来包裹行为仿真的代码

// synopsys translate_off 
// the code doNOT get into synthesis   
// synopsys translate_on    

上面两个编译命令中的Verilog代码不参加电路转换,但是可以参加RTL的仿真。本模块在其中添加了RAM内容初始化的代码,这样做是为了避免进行RTL仿真时从RAM中读出未知的“X”数据。但是,请注意,这仅仅是用于仿真,实际的FPGA中的RAM,在上电以后,未向其写入数据之前,RAM中存储单元的数值具体是什么要看厂家的芯片手册。

电路设计内容

这种双口RAM的读写可以是异步的,即写入时钟和读出时钟可以是异步的,所以可以使用这种RAM跨越时钟域或是构建一个FIFO(也是用了跨越时钟域的)

参考代码


// module top, a dual port RAM 
// instancing and define parameter of dpram module
module top(
  WE      ,   // write enable
  WCLK    ,   // write clock
  RCLK    ,   // read clock   
  WA      ,   // write address
  RA      ,   // read address
  WD      ,   // write data
  RD      );  // read data

parameter DATAWL    = 8;
parameter ADDRWL    = 8;
parameter C2Q       = 2;

input                    WE, WCLK, RCLK;
input   [ADDRWL -1:0]    WA, RA; 
input   [DATAWL -1:0]    WD;
output  [DATAWL -1:0]    RD;

dpram U0_dpram(
  .WE      (WE      ),   // write enable
  .WCLK    (WCLK    ),   // write clock
  .RCLK    (RCLK    ),   // read clock   
  .WA      (WA      ),   // write address
  .RA      (RA      ),   // read address
  .WD      (WD      ),   // write data
  .RD      (RD      ));  // read data

defparam U0_dpram.DATAWL    = DATAWL;
defparam U0_dpram.ADDRWL    = ADDRWL;
defparam U0_dpram.C2Q       = C2Q   ;

endmodule
// endmodule top

module dpram(
  WE      ,   // write enable
  WCLK    ,   // write clock
  RCLK    ,   // read clock   
  WA      ,   // write address
  RA      ,   // read address
  WD      ,   // write data
  RD      );  // read data
// external set param
parameter DATAWL    = 0;
parameter ADDRWL    = 0;
parameter C2Q       = 0;
  input                    WE, WCLK, RCLK;
  input   [ADDRWL -1:0]    WA, RA; 
  input   [DATAWL -1:0]    WD;
  output  [DATAWL -1:0]    RD;

  reg[DATAWL-1:0] RD;
  reg[DATAWL-1:0] mem [(1 << ADDRWL)-1:0];

  always @ (posedge WCLK) begin
    if(WE)
      mem[WA] <= #C2Q WD;
  end
  always @ (posedge RCLK) begin
    RD  <= #C2Q mem[RA];
  end
// ######################################  
// synopsys translate_off                  
// ######################################  
// the code below this line will NOT take part into synthesis
// they are only needed by RTL simulation

  // task DumpDpRAM, get the content of RAM[addr]
  task DumpDpRAM; 
  input     [ADDRWL-1  :0] addr    ;
  output    [DATAWL-1 :0]  content ;
  begin
    content = mem[addr];
  end       // task begin
  endtask   // task DumpDpRAM

  // task RAMInit, initialize the RAM content
  task RAMInit;
    integer i;
    reg[DATAWL-1:0] initData;
  begin
    initData = 'hAAAA;
    // initData = (1 << DATAWL) - 1;
    for( i = 0; i < (1 << ADDRWL); i = i + 1)
      mem[i] = initData;
  end
  endtask
  initial begin
    RAMInit();
    $display("module dpram().RAMInit()called @ %0d", $time);
  end
// ###################################### 
// synopsys translate_on                  
// ###################################### 
// the code below this line will take part in synthesis
endmodule   // module dptbram() 

思考与问题

ROM

知识点介绍

在Altera中的ROM是使用MemoryBlock实现的,因为MemoryBlock拥有内容初始化的能力。本代码的最终编译成为一个FPGA片内的RAM需要以下的支持

电路设计内容

参考代码

// module top, a synchronized rom
module top(
  CLK   ,        // clock
  RA    ,        // read address
  RD    );       // read data
input          CLK;
input  [6  :0] RA;
output [12 :0] RD;
reg    [12 :0] RD;
always @ (posedge CLK)
  case(RA)
     7 'd 0     : RD = #1 13'd 0     ; // 0x0      
     7 'd 1     : RD = #1 13'd 101   ; // 0x65     
     7 'd 2     : RD = #1 13'd 201   ; // 0xC9     
     7 'd 3     : RD = #1 13'd 301   ; // 0x12D    
        ... ... ...
     7 'd 123   : RD = #1 13'd 8176  ; // 0x1FF0   
     7 'd 124   : RD = #1 13'd 8181  ; // 0x1FF5   
     7 'd 125   : RD = #1 13'd 8185  ; // 0x1FF9   
     7 'd 126   : RD = #1 13'd 8189  ; // 0x1FFD   
     7 'd 127   : RD = #1 13'd 8190  ; // 0x1FFE   
  endcase
endmodule

思考与问题

寄存器组和多重例化

使用多次例化语法生成的寄存器组RTL结构图

参考代码


// module top, example for, 'for' and 'generate'
module top(
  CLK   ,
  RST   ,
  IN1   ,
  OUT1  );       
parameter DWL = 8;
parameter reg_len = 3;
input CLK;
input RST;
input  [DWL-1:0] IN1;
output [DWL-1:0] OUT1;

reg  [DWL-1:0] in1R[reg_len-1:0];
wire [DWL-1:0] in1R_inW[reg_len-1:0];
integer idx;
always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    for(idx = 0; idx < reg_len; idx = idx +1) begin
      in1R[idx] <= 0;
    end
  end
  else begin
    for(idx = 0; idx < reg_len; idx = idx +1) begin
      in1R[idx] <= in1R_inW[idx];
    end
  end
end

genvar i_g;
generate
for (i_g =0; i_g < reg_len; i_g=i_g+1) begin: MULT_INST
  wire [DWL-1:0] logic_inW;
  if(i_g == 0) begin
    assign logic_inW = IN1;
  end
  else begin
    assign logic_inW = in1R[i_g-1];
  end
  param_logic U_pl(
    .IN    (logic_inW      ), 
    .OUT   (in1R_inW[i_g] ));
  defparam U_pl.DWL     = DWL;
  defparam U_pl.INST_P1 = i_g;
end
endgenerate
assign OUT1 = in1R[reg_len-1];
endmodule 

module param_logic(
  IN    , 
  OUT   );
parameter DWL     = 0;
parameter INST_P1 = 0;
input  [DWL-1:0] IN  ;
output [DWL-1:0] OUT ;
assign OUT = IN + INST_P1;
endmodule