IC 设计工具篇 -- 寄存器模块生成脚本设计过程(verilog 版)

简介

能让电脑完成的就不要手写了,自动生成寄存器模块verilog代码的脚本设计过程

不知道大家是否向我一样,需要快速的实现模块。
我大部分写的都是算法模块,而且算法每次修改都特别大,除了控制部分可以复用以外其他都需要重新设计。
但是有些模块还是有写规律可循的,比如寄存器部分。
不管算法如何改变寄存器的配置方式可以说都是差不多的,那就可以让电脑代替我来完成这部分工作了。

脚本功能

  • 直接生成寄存器模块的verilog文件
  • 配置接口采用apb接口,32bit数据和16bit地址
  • 支持 RW、RO 类型的寄存器
  • 支持地址自动分配
  • 支持地址和bit位粘连检查

基础知识

  • python
  • verilog

python现在主要有两个大版本2.x 和 3.x ,linux 现在大部分默认还是python2,所以下面所有的都以python2为基础
python 2.x 和 3.x 有些许不同,请大家注意
建议使用2.7和3.4及其以上版本

代码格式

先定义下最后生成的代码风格(我就以我平时喜欢的风格来设计了)
代码中有下面两类寄存器
1.可读可写的寄存器:base,test
2.只读寄存器:dbg

module Mbase_reg_cfg
(
  // clk / rst_n
   input                    i_clk
  ,input                    i_rst_n

  // apb port
  ,input                    i_psel
  ,input  [15:0]            i_paddr
  ,input                    i_penable
  ,input                    i_pwrite
  ,input  [31:0]            i_pwdata
  ,output [31:0]            o_prdata

  // output port
  ,output                   o_sw_base_0
  ,output [3:0]             o_sw_base_1
  ,output [3:0]             o_sw_tt_dbg_1
  ,output                   o_sw_tt_dbg_0

  // input port
  ,input  [3:0]             i_ro_dbg_1
  ,input  [6:0]             i_ro_dbg_0

);
//==========================================================
// apb bus ctrl                                 start/*{{{*/
//==========================================================
wire                        wr                             ;
wire                        rd                             ;
wire      [15:0]            addr                           ;
wire      [31:0]            wdata                          ;
reg       [31:0]            rdata                          ;

assign wr       = i_psel &  i_penable &  i_pwrite          ;
assign rd       = i_psel & ~i_penable & ~i_pwrite          ;
assign addr     = i_paddr                                  ;
assign wdata    = i_pwdata                                 ;
assign o_prdata = rdata                                    ;
//==========================================================
// apb bus ctrl                                   end/*}}}*/
//==========================================================

//==========================================================
// localparam                                   start/*{{{*/
//==========================================================
localparam BASE_ADDR                            = 16'h0000 ;
localparam DBG_ADDR                             = 16'h0004 ;
localparam TEST_ADDR                            = 16'h0008 ;
//==========================================================
// localparam                                     end/*}}}*/
//==========================================================

//==========================================================
// reg and wire                                 start/*{{{*/
//==========================================================

// write signal
wire        wr_base_w                                      ;
wire        wr_test_w                                      ;

// reg
reg  [31:0] reg_base_r                                     ;
reg  [31:0] reg_test_r                                     ;

// read data
wire [31:0] rdata_base_w                                   ;
wire [31:0] rdata_dbg_w                                    ;
wire [31:0] rdata_test_w                                   ;

//==========================================================
// reg and wire                                   end/*}}}*/
//==========================================================

//==========================================================
// write signal gen                             start/*{{{*/
//==========================================================
assign wr_base_w            = wr & (addr == BASE_ADDR     );
assign wr_test_w            = wr & (addr == TEST_ADDR     );
//==========================================================
// write signal gen                               end/*}}}*/
//==========================================================

//==========================================================
// base                                         start/*{{{*/
//==========================================================
localparam BASE_VALID_BIT                 = 32'h00_00_00_f1;
localparam BASE_DEFAULT                   = 32'h00_00_00_f1;

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    reg_base_r <= BASE_DEFAULT;
  end
  else if(wr_base_w) begin
    reg_base_r <= wdata & BASE_VALID_BIT;
  end
end

assign o_sw_base_0      = reg_base_r[0];
assign o_sw_base_1      = reg_base_r[7:4];

assign rdata_base_w = {
    24'd0
   ,o_sw_base_1
   ,3'd0
   ,o_sw_base_0
};
//==========================================================
// base                                           end/*}}}*/
//==========================================================

//==========================================================
// dbg                                          start/*{{{*/
//==========================================================
assign rdata_dbg_w = {
    14'd0
   ,i_ro_dbg_0
   ,3'd0
   ,i_ro_dbg_1
   ,4'd0
};
//==========================================================
// dbg                                            end/*}}}*/
//==========================================================

//==========================================================
// test                                         start/*{{{*/
//==========================================================
localparam TEST_VALID_BIT                 = 32'h80_00_00_f0;
localparam TEST_DEFAULT                   = 32'h00_00_00_00;

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    reg_test_r <= TEST_DEFAULT;
  end
  else if(wr_test_w) begin
    reg_test_r <= wdata & TEST_VALID_BIT;
  end
end

assign o_sw_tt_dbg_1    = reg_test_r[7:4];
assign o_sw_tt_dbg_0    = reg_test_r[31];

assign rdata_test_w = {
    o_sw_tt_dbg_0
   ,23'd0
   ,o_sw_tt_dbg_1
   ,4'd0
};
//==========================================================
// test                                           end/*}}}*/
//==========================================================

//==========================================================
// rdata                                        start/*{{{*/
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rdata <= 32'd0;
  end
  else if(rd) begin
    case(addr)
    BASE_ADDR              : rdata <= rdata_base_w         ;
    DBG_ADDR               : rdata <= rdata_dbg_w          ;
    TEST_ADDR              : rdata <= rdata_test_w         ;
    default                : rdata <= 32'd0                ;
    endcase
  end
  else begin
    rdata <= rdata;
  end
end
//==========================================================
// rdata                                          end/*}}}*/
//==========================================================

endmodule

生成用列表文件内容

产生寄存器文件需要知道有那些寄存器和如何拼接
下面为产生上面寄存器文件所使用的列表文件格式
生成模块名M<name>_reg_cfg
列表文件名<name>_reg_list
列表列表名<name>_reg_list

  • 如果列表文件和列表名不一致或是不匹配会出现问题
base_reg_list = [
{
  "name" : "base",
  "addr" : 0x0000,
  "type" : "RW",                # "RW" or "RO"
  "bit"  : [
    ["base_0", [0], 0x1],       # [寄存器名称, 使用bit位, 默认值]
    ["base_1", [7,4], 0xf],     # [寄存器名称, 使用bit位, 默认值]
  ]
},
{
  "name" : "dbg",
  # "addr" : 0x0004,            # 地址可以不显示指定,默认自增
  "type" : "RO",
  "bit"  : [
    ["dbg_1", [7,4]],
    ["dbg_0", [17,11], 0x0],    # 只读寄存器默认值不起作用
  ]
},
{
  "name" : "test",
  "addr" : 0x0008,
  # "type" : "RW",              # 类型可以不显示指定,默认 "RW"
  "bit"  : [
    ["tt_dbg_1", [7,4]],        # 默认值可以不显示指定,默认 "0"
    ["tt_dbg_0", [31], 0x0],
  ]
},
]

脚本处理步骤

1.读取寄存器列表
2.对没有制定默认值的地方进行添加,没有地址的使用前面进行累加
3.检查地址、bit位有无粘连
4.产生输入输出端口
5.产生localparam 地址内容
6.产生后续会使用到的信号定义
7.产生寄存器写使能逻辑
8.产生每个寄存器的写过程和输出信号拆分和读取信号合并内容
9.产生寄存器读逻辑
10.合并上述内容进行输出


脚本设计过程问题与关键点

1.读取寄存器列表

主要读取配置文件并生成脚本识别格式
为了省事,就不对寄存器列表进行语法解析,而是使用python的exec函数
exec : 执行字符串中的python语句

def get_reg_list(file_name):
  with open(file_name, "r") as f:
    data_str = f.readlines()
    data_str = "".join(data_str)

    _locals = locals()
    exec(data_str, globals(), _locals)
    data = _locals[file_name]

    return data

上面主要步骤

  • 打开文件并读取全部内容,将文件列表拼接为一个大的字符串
  • 执行字符串中内容,将字符串中的寄存器列表返回

2.对没有制定默认值的地方进行添加,没有地址的使用前面进行累加

为了后面代码不会每次还判读时候有指定,没有指定用默认值。
所以在给到后面处理时将没有显示指定的地方添加上默认值,简化后面代码处理
前面定义了地址默认自增,类型默认只读,默认值为0.
下面代码有相应的操作

def add_default(data):
  def_addr  = 0x0000
  def_type  = "RW"
  def_value = 0x0

  for reg in data:
    # add default addr
    try:
      def_addr = reg["addr"] + 4
    except:
      reg["addr"] = def_addr
      def_addr = reg["addr"] + 4

    # add default type
    try:
      reg["type"]
    except:
      reg["type"] = def_type

    for bit in reg["bit"]:
      # add default range
      if len(bit[1]) == 1:
        bit[1] = [bit[1][0],bit[1][0]]

      # add default value
      try:
        bit[2]
      except:
        bit.append(def_value)

上面代码中这两行不知道你有没有想到为什么这么做。

      if len(bit[1]) == 1:
        bit[1] = [bit[1][0],bit[1][0]]

因为在寄存器只有一个bit的情况下,我们列表中只有一个值
为了将多bit和单bit统一,所以将单bit转为和多bit相同的方式

3.检查地址、bit位有无粘连

在设计过程中我一直有一个观点:人是最不靠谱的
在上面我们手动需要写的就是list文件
因为是手写,没有办法保证100%正确,所以需要添加适当的检查机制,确保正确性
常见的问题有几点:地址重叠、bit重叠、bit超出范围
下面代码就是针对这几点进行的检查

def check_overlap(data):
  err = 0
  
  # addr overlap check
  addr_list = []
  for reg in data:
    addr_list.append(reg["addr"])
  set_addr_list = list(set(addr_list))
  if len(addr_list) != len(set_addr_list):
    overlap_addr = [ addr for addr in set_addr_list if addr_list.count(addr) > 1]
    for addr in overlap_addr:
      print("Err  : 0x%08x addr overlap !!!" % addr)
      err = 1

  # bit overlap check
  for reg in data:
    bit_list = []
    for bit in reg["bit"]:
      bit_start = bit[1][1]
      bit_end   = bit[1][0]
      bit_list += range(bit_start,bit_end+1)
    set_bit_list = list(set(bit_list))
    if len([bit for bit in set_bit_list if bit >= 32]) > 0:
      print("Err  : reg %s addr 0x%08x bit out range !!!" % (reg['name'], reg['addr']))
      err = 1

    if len(bit_list) != len(set_bit_list):
      overlap_bit = [ bit for bit in set_bit_list if bit_list.count(bit) > 1]
      print("Err  : 0x%08x bit %s overlap !!!" % (reg['addr'], str(overlap_bit)))
      err = 1

  if err:
    sys.exit(1)

4.产生输入输出端口

接下来就该进入正式的verilog代码部分了
首先我们先确定需要产生的部分(下面内容为上面代码格式部分)

  // output port
  ,output                   o_sw_base_0
  ,output [3:0]             o_sw_base_1
  ,output [3:0]             o_sw_tt_dbg_1
  ,output                   o_sw_tt_dbg_0

  // input port
  ,input  [3:0]             i_ro_dbg_1
  ,input  [6:0]             i_ro_dbg_0

这部分是我们使用过程中需要的输入输出端口
所有的模块我们都需要配置相应的寄存器来确定工作模式或是开关
上面输出口就是通过总线配置后的值
输入一般为运行后的输出,或是debug用的
下面为详细的产生代码

def gen_io_port(data):
  io_str = []
  rw_list = [ reg for reg in data if reg['type'] == 'RW']
  ro_list = [ reg for reg in data if reg['type'] == 'RO']

  # output port
  io_str.append("  // output port\n")
  for reg in rw_list:
    for bit in reg['bit']:
      bit_range = bit[1]
      bit_len   = bit_range[0] - bit_range[1]

      if bit_len != 0:
        range_str = "[%d:0]" % bit_len
      else:
        range_str = ""

      bit_name_str = "o_sw_%s" % bit[0]

      io_str.append("  ,output %-17s %s\n" % (range_str, bit_name_str,))
  io_str.append("\n");

  # input port
  io_str.append("  // input port\n")
  for reg in ro_list:
    for bit in reg['bit']:
      bit_range = bit[1]
      bit_len   = bit_range[0] - bit_range[1]

      if bit_len != 0:
        range_str = "[%d:0]" % bit_len
      else:
        range_str = ""

      bit_name_str = "i_ro_%s" % bit[0]

      io_str.append("  ,input  %-17s %s\n" % (range_str, bit_name_str,))
  io_str.append("\n");

  return io_str

5.产生localparam 地址内容

接下来就改产生地址信息了,这里使用lcalparam定义地址,然后使用
所以这里就只产生定义,具体使用在后面的写逻辑生成
下面为需要产生的部分(下面内容为上面代码格式部分)

localparam BASE_ADDR                            = 16'h0000 ;
localparam DBG_ADDR                             = 16'h0004 ;
localparam TEST_ADDR                            = 16'h0008 ;

下面为产生代码。
这里没有什么难点

def gen_localparam(data):
  localparam_str = []
  for reg in data:
    addr_name = "%s_ADDR" % reg['name'].upper()
    addr_str  = "16'h%04x" % reg['addr']

    localparam_str.append("localparam %-36s = %s ;\n" % (addr_name, addr_str,))

  return localparam_str

6.产生后续会使用到的信号定义

下面就是信号的定义部分
这里主要定义了:写使能信号、寄存器信号、读数据信号

// write signal
wire        wr_base_w                                      ;
wire        wr_test_w                                      ;

// reg
reg  [31:0] reg_base_r                                     ;
reg  [31:0] reg_test_r                                     ;

// read data
wire [31:0] rdata_base_w                                   ;
wire [31:0] rdata_dbg_w                                    ;
wire [31:0] rdata_test_w                                   ;

写使能和寄存器信号都是"RW"的才有,而读数据信号是所有类型的都有
主要是因为"RO"的不需要写,数据直接通过输入进来,只需要总线能读取即可
下面为相应产生部分

def gen_reg_wire(data):
  reg_wire_str = []
  rw_list = [ reg for reg in data if reg['type'] == 'RW']

  reg_wire_str.append("\n")
  # write signal
  reg_wire_str.append("// write signal\n")
  for reg in rw_list:
    wire_name = "wr_%s_w" % reg['name']

    reg_wire_str.append("wire        %-47s;\n" % wire_name)
  reg_wire_str.append("\n")

  # reg
  reg_wire_str.append("// reg\n")
  for reg in rw_list:
    reg_name = "reg_%s_r" % reg['name']

    reg_wire_str.append("reg  [31:0] %-47s;\n" % reg_name)
  reg_wire_str.append("\n")

  # read data
  reg_wire_str.append("// read data\n")
  for reg in data:
    wire_name = "rdata_%s_w" % reg['name']

    reg_wire_str.append("wire [31:0] %-47s;\n" % wire_name)
  reg_wire_str.append("\n")

  return reg_wire_str

7.产生寄存器写使能逻辑

接下来就是产生写使能逻辑

assign wr_base_w            = wr & (addr == BASE_ADDR     );
assign wr_test_w            = wr & (addr == TEST_ADDR     );

这里只需要产生RW类型的写使能即可,RO不需要

def gen_write_signal(data):
  write_signal_str = []
  rw_list = [ reg for reg in data if reg['type'] == 'RW']

  for reg in rw_list:
    wire_name = "wr_%s_w" % reg['name']
    addr_name = "%s_ADDR" % reg['name'].upper()

    write_signal_str.append("assign %-20s = wr & (addr == %-14s);\n" % (wire_name, addr_name,))

  return write_signal_str

8.产生每个寄存器的写过程和输出信号拆分和读取信号合并内容

接下来就是最主要的部分了
寄存器的读写、输出连接、读拼接都是在这里做的
下面为两类寄存器生成的最终生成内容(生成的注释中有vim识别的折叠,这是个人习惯)

//==========================================================
// base                                         start/*{{{*/
//==========================================================
localparam BASE_VALID_BIT                 = 32'h00_00_00_f1;
localparam BASE_DEFAULT                   = 32'h00_00_00_f1;

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    reg_base_r <= BASE_DEFAULT;
  end
  else if(wr_base_w) begin
    reg_base_r <= wdata & BASE_VALID_BIT;
  end
end

assign o_sw_base_0      = reg_base_r[0];
assign o_sw_base_1      = reg_base_r[7:4];

assign rdata_base_w = {
    24'd0
   ,o_sw_base_1
   ,3'd0
   ,o_sw_base_0
};
//==========================================================
// base                                           end/*}}}*/
//==========================================================

//==========================================================
// dbg                                          start/*{{{*/
//==========================================================
assign rdata_dbg_w = {
    14'd0
   ,i_ro_dbg_0
   ,3'd0
   ,i_ro_dbg_1
   ,4'd0
};
//==========================================================
// dbg                                            end/*}}}*/
//==========================================================

下面代码内容较多,对照上面生成内容看会更好

def gen_reg(data):
  reg_str = []
  for reg in data:
    reg_str.append("//==========================================================\n")
    reg_str.append("// %-44s start/*{{{*/\n" % reg['name'])
    reg_str.append("//==========================================================\n")

    reg_name       = "reg_%s_r"     % reg['name']
    value_bit_name = "%s_VALID_BIT" % reg['name'].upper()
    default_name   = "%s_DEFAULT"   % reg['name'].upper()
    write_name     = "wr_%s_w"      % reg['name']
    rdata_name     = "rdata_%s_w"   % reg['name']
    if reg['type'] == 'RW':
      # localparam
      value_bit = 0
      default   = 0
      for bit in reg['bit']:
        bit_range = bit[1]
        bit_def   = bit[2]
        bit_start = bit_range[1]
        bit_end   = bit_range[0]
        bit_len   = bit_end - bit_start + 1
        value_bit |= (((2**bit_len) - 1) << bit_start)
        default   |= bit_def << bit_start

      value_bit_str = "%08x" % value_bit
      default_str   = "%08x" % default

      value_bit_str = "_".join([ value_bit_str[i*2:i*2+2] for i in range(4)])
      default_str   = "_".join([ default_str[i*2:i*2+2] for i in range(4)])

      reg_str.append("localparam %-30s = 32'h%s;\n" % (value_bit_name, value_bit_str))
      reg_str.append("localparam %-30s = 32'h%s;\n" % (default_name, default_str))
      reg_str.append("\n")

      # reg function
      reg_str.append("always@(posedge i_clk) begin\n" )
      reg_str.append("  if(i_rst_n == 1'd0) begin\n"  )
      reg_str.append("    %s <= %s;\n"                % (reg_name, default_name,))
      reg_str.append("  end\n"                        )
      reg_str.append("  else if(%s) begin\n"          % write_name)
      reg_str.append("    %s <= wdata & %s;\n"        % (reg_name, value_bit_name,))
      reg_str.append("  end\n"                        )
      reg_str.append("end\n"                          )
      reg_str.append("\n"                             )

      # output
      for bit in reg['bit']:
        output_name = "o_sw_%s" % bit[0]
        bit_range   = bit[1]
        bit_start   = bit_range[1]
        bit_end     = bit_range[0]
        bit_len     = bit_end - bit_start + 1

        if bit_len > 1:
          bit_range_str = "[%d:%d]" % (bit_end, bit_start)
        else:
          bit_range_str = "[%d]" % bit_start

        reg_str.append("assign %-16s = %s%s;\n" % (output_name, reg_name, bit_range_str,))
      reg_str.append("\n")

    # rdata
    bit_index  = 0
    rdata_list = []
    for bit in reg['bit']:
      bit_range   = bit[1]
      bit_start   = bit_range[1]
      bit_end     = bit_range[0]

      if bit_index != bit_start:
        rdata_list.append("%d'd0" % (bit_start - bit_index))
      if reg['type'] == "RW":
        rdata_list.append("o_sw_%s" % bit[0])
      else:
        rdata_list.append("i_ro_%s" % bit[0])
      bit_index = bit_end + 1
    if bit_index != 32:
      rdata_list.append("%d'd0" % (32 - bit_index))

    reg_str.append("assign %s = {\n" % rdata_name)
    reg_str.append("    " + "\n   ,".join(rdata_list[::-1]) + "\n")
    reg_str.append("};\n")

      
    reg_str.append("//==========================================================\n")
    reg_str.append("// %-44s   end/*}}}*/\n" % reg['name'])
    reg_str.append("//==========================================================\n")
    reg_str.append("\n")

  return reg_str

首先产生需要一些名称
产生默认值、可写bit 这两个localparam
产生寄存器写的always
产生输出信号的连接
产生输出信号的拼接,这里RW拼接的是o_sw_* 而RO拼接的是i_ro_*

9.产生寄存器读逻辑

这可以说已经是最后了
前面大部分逻辑都生成了,就差能让总线读取到的逻辑了

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rdata <= 32'd0;
  end
  else if(rd) begin
    case(addr)
    BASE_ADDR              : rdata <= rdata_base_w         ;
    DBG_ADDR               : rdata <= rdata_dbg_w          ;
    TEST_ADDR              : rdata <= rdata_test_w         ;
    default                : rdata <= 32'd0                ;
    endcase
  end
  else begin
    rdata <= rdata;
  end
end

这里恒简单,因为前面已经将信号拼接好了,这里直接使用即可

def gen_rdata(data):
  rdata_str = []
  for reg in data:
    addr_name  = "%s_ADDR"    % reg['name'].upper()
    rdata_name = "rdata_%s_w" % reg['name']

    rdata_str.append("    %-22s : rdata <= %-21s;\n" % (addr_name, rdata_name,))
    
  return rdata_str

10.合并上述内容进行输出

上面产生完所有逻辑内容,这里就只需要将所有内容拼接起来
下面代码虽然长,但其实没有什么内容。

def gen_reg_file(
    mode_name,
    io_str,
    localparam_str,
    reg_wire_str,
    write_signal_str,
    reg_str,
    rdata_str,
    ):
  with open("../reg_file/%s_reg_cfg.v" % mode_name, "w") as f:
    f.write("module M%s_reg_cfg\n" % mode_name                              )
    f.write("(\n"                                                           )
    f.write("  // clk / rst_n\n"                                            )
    f.write("   input                    i_clk\n"                           )
    f.write("  ,input                    i_rst_n\n"                         )
    f.write("\n"                                                            )
    f.write("  // apb port\n"                                               )
    f.write("  ,input                    i_psel\n"                          )
    f.write("  ,input  [15:0]            i_paddr\n"                         )
    f.write("  ,input                    i_penable\n"                       )
    f.write("  ,input                    i_pwrite\n"                        )
    f.write("  ,input  [31:0]            i_pwdata\n"                        )
    f.write("  ,output [31:0]            o_prdata\n"                        )
    f.write("\n"                                                            )
    f.write("".join(io_str))
    f.write(");\n"                                                          )
    f.write("//==========================================================\n")
    f.write("// apb bus ctrl                                 start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("wire                        wr                             ;\n")
    f.write("wire                        rd                             ;\n")
    f.write("wire      [15:0]            addr                           ;\n")
    f.write("wire      [31:0]            wdata                          ;\n")
    f.write("reg       [31:0]            rdata                          ;\n")
    f.write("\n"                                                            )
    f.write("assign wr       = i_psel &  i_penable &  i_pwrite          ;\n")
    f.write("assign rd       = i_psel & ~i_penable & ~i_pwrite          ;\n")
    f.write("assign addr     = i_paddr                                  ;\n")
    f.write("assign wdata    = i_pwdata                                 ;\n")
    f.write("assign o_prdata = rdata                                    ;\n")
    f.write("//==========================================================\n")
    f.write("// apb bus ctrl                                   end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("//==========================================================\n")
    f.write("// localparam                                   start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("".join(localparam_str))
    f.write("//==========================================================\n")
    f.write("// localparam                                     end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("//==========================================================\n")
    f.write("// reg and wire                                 start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("".join(reg_wire_str))
    f.write("//==========================================================\n")
    f.write("// reg and wire                                   end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("//==========================================================\n")
    f.write("// write signal gen                             start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("".join(write_signal_str))
    f.write("//==========================================================\n")
    f.write("// write signal gen                               end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("".join(reg_str))
    f.write("//==========================================================\n")
    f.write("// rdata                                        start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("always@(posedge i_clk) begin\n"                                )
    f.write("  if(i_rst_n == 1'd0) begin\n"                                 )
    f.write("    rdata <= 32'd0;\n"                                         )
    f.write("  end\n"                                                       )
    f.write("  else if(rd) begin\n"                                         )
    f.write("    case(addr)\n"                                              )
    f.write("".join(rdata_str))
    f.write("    default                : rdata <= 32'd0                ;\n")
    f.write("    endcase\n"                                                 )
    f.write("  end\n"                                                       )
    f.write("  else begin\n"                                                )
    f.write("    rdata <= rdata;\n"                                         )
    f.write("  end\n"                                                       )
    f.write("end\n"                                                         )
    f.write("//==========================================================\n")
    f.write("// rdata                                          end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("endmodule\n"                                                   )

    f.close()

结尾

在工作过程中有许多的事,而且IC这一行容错率非常低,稍微一个bug就可以是流片失败(FPGA的除外)
而我一直奉行<人是最不靠谱的>观点。而工作后会发现有很多的工具就是专门为了帮助人们找错误的,仿真就是其一。
然而工具使用的在熟练,也是需要花费不少时间的。
所以提高效率和准确性就是流程化,并且自动化

下面为完整的脚本

#!/bin/env python
import os
import sys

# ==========================================================
#  get reg list                                    start#{{{
# ==========================================================
def get_reg_list(file_name):
  try:
    with open(file_name, "r") as f:
      data_str = f.readlines()
      data_str = "".join(data_str)

      _locals = locals()
      exec(data_str, globals(), _locals)
      data = _locals[file_name]

      return data
  except:
    print("open file %s err" % file_name)
    sys.exit(1)
# ==========================================================
#  get reg list                                      end#}}}
# ==========================================================

# ==========================================================
#  add default                                     start#{{{
# ==========================================================
def add_default(data):
  def_addr  = 0x0000
  def_type  = "RW"
  def_value = 0x0

  for reg in data:
    # add default addr
    try:
      def_addr = reg["addr"] + 4
    except:
      reg["addr"] = def_addr
      def_addr = reg["addr"] + 4

    # add default type
    try:
      reg["type"]
    except:
      reg["type"] = def_type

    for bit in reg["bit"]:
      # add default range
      if len(bit[1]) == 1:
        bit[1] = [bit[1][0],bit[1][0]]

      # add default value
      try:
        bit[2]
      except:
        bit.append(def_value)
# ==========================================================
#  add default                                       end#}}}
# ==========================================================

# ==========================================================
#  check overlap                                   start#{{{
# ==========================================================
def check_overlap(data):
  err = 0
  
  # addr overlap check
  addr_list = []
  for reg in data:
    addr_list.append(reg["addr"])
  set_addr_list = list(set(addr_list))
  if len(addr_list) != len(set_addr_list):
    overlap_addr = [ addr for addr in set_addr_list if addr_list.count(addr) > 1]
    for addr in overlap_addr:
      print("Err  : 0x%08x addr overlap !!!" % addr)
      err = 1

  # bit overlap check
  for reg in data:
    bit_list = []
    for bit in reg["bit"]:
      bit_start = bit[1][1]
      bit_end   = bit[1][0]
      bit_list += range(bit_start,bit_end+1)
    set_bit_list = list(set(bit_list))
    if len([bit for bit in set_bit_list if bit >= 32]) > 0:
      print("Err  : reg %s addr 0x%08x bit out range !!!" % (reg['name'], reg['addr']))
      err = 1

    if len(bit_list) != len(set_bit_list):
      overlap_bit = [ bit for bit in set_bit_list if bit_list.count(bit) > 1]
      print("Err  : 0x%08x bit %s overlap !!!" % (reg['addr'], str(overlap_bit)))
      err = 1

  if err:
    sys.exit(1)
# ==========================================================
#  check overlap                                     end#}}}
# ==========================================================

# ==========================================================
#  gen io port                                     start#{{{
# ==========================================================
def gen_io_port(data):
  io_str = []
  rw_list = [ reg for reg in data if reg['type'] == 'RW']
  ro_list = [ reg for reg in data if reg['type'] == 'RO']

  # output port
  io_str.append("  // output port\n")
  for reg in rw_list:
    for bit in reg['bit']:
      bit_range = bit[1]
      bit_len   = bit_range[0] - bit_range[1]

      if bit_len != 0:
        range_str = "[%d:0]" % bit_len
      else:
        range_str = ""

      bit_name_str = "o_sw_%s" % bit[0]

      io_str.append("  ,output %-17s %s\n" % (range_str, bit_name_str,))
  io_str.append("\n");

  # input port
  io_str.append("  // input port\n")
  for reg in ro_list:
    for bit in reg['bit']:
      bit_range = bit[1]
      bit_len   = bit_range[0] - bit_range[1]

      if bit_len != 0:
        range_str = "[%d:0]" % bit_len
      else:
        range_str = ""

      bit_name_str = "i_ro_%s" % bit[0]

      io_str.append("  ,input  %-17s %s\n" % (range_str, bit_name_str,))
  io_str.append("\n");

  return io_str

# ==========================================================
#  gen io port                                       end#}}}
# ==========================================================

# ==========================================================
#  gen localparam                                  start#{{{
# ==========================================================
def gen_localparam(data):
  localparam_str = []
  for reg in data:
    addr_name = "%s_ADDR" % reg['name'].upper()
    addr_str  = "16'h%04x" % reg['addr']

    localparam_str.append("localparam %-36s = %s ;\n" % (addr_name, addr_str,))

  return localparam_str

# ==========================================================
#  gen localparam                                    end#}}}
# ==========================================================

# ==========================================================
#  gen reg wire                                    start#{{{
# ==========================================================
def gen_reg_wire(data):
  reg_wire_str = []
  rw_list = [ reg for reg in data if reg['type'] == 'RW']

  reg_wire_str.append("\n")
  # write signal
  reg_wire_str.append("// write signal\n")
  for reg in rw_list:
    wire_name = "wr_%s_w" % reg['name']

    reg_wire_str.append("wire        %-47s;\n" % wire_name)
  reg_wire_str.append("\n")

  # reg
  reg_wire_str.append("// reg\n")
  for reg in rw_list:
    reg_name = "reg_%s_r" % reg['name']

    reg_wire_str.append("reg  [31:0] %-47s;\n" % reg_name)
  reg_wire_str.append("\n")

  # read data
  reg_wire_str.append("// read data\n")
  for reg in data:
    wire_name = "rdata_%s_w" % reg['name']

    reg_wire_str.append("wire [31:0] %-47s;\n" % wire_name)
  reg_wire_str.append("\n")

  return reg_wire_str
# ==========================================================
#  gen reg wire                                      end#}}}
# ==========================================================

# ==========================================================
#  gen write signal                                start#{{{
# ==========================================================
def gen_write_signal(data):
  write_signal_str = []
  rw_list = [ reg for reg in data if reg['type'] == 'RW']

  for reg in rw_list:
    wire_name = "wr_%s_w" % reg['name']
    addr_name = "%s_ADDR" % reg['name'].upper()

    write_signal_str.append("assign %-20s = wr & (addr == %-14s);\n" % (wire_name, addr_name,))

  return write_signal_str
# ==========================================================
#  gen write signal                                  end#}}}
# ==========================================================

# ==========================================================
#  gen reg                                         start#{{{
# ==========================================================
def gen_reg(data):
  reg_str = []
  for reg in data:
    reg_str.append("//==========================================================\n")
    reg_str.append("// %-44s start/*{{{*/\n" % reg['name'])
    reg_str.append("//==========================================================\n")

    reg_name       = "reg_%s_r"     % reg['name']
    value_bit_name = "%s_VALID_BIT" % reg['name'].upper()
    default_name   = "%s_DEFAULT"   % reg['name'].upper()
    write_name     = "wr_%s_w"      % reg['name']
    rdata_name     = "rdata_%s_w"   % reg['name']
    if reg['type'] == 'RW':
      # localparam
      value_bit = 0
      default   = 0
      for bit in reg['bit']:
        bit_range = bit[1]
        bit_def   = bit[2]
        bit_start = bit_range[1]
        bit_end   = bit_range[0]
        bit_len   = bit_end - bit_start + 1
        value_bit |= (((2**bit_len) - 1) << bit_start)
        default   |= bit_def << bit_start

      value_bit_str = "%08x" % value_bit
      default_str   = "%08x" % default

      value_bit_str = "_".join([ value_bit_str[i*2:i*2+2] for i in range(4)])
      default_str   = "_".join([ default_str[i*2:i*2+2] for i in range(4)])

      reg_str.append("localparam %-30s = 32'h%s;\n" % (value_bit_name, value_bit_str))
      reg_str.append("localparam %-30s = 32'h%s;\n" % (default_name, default_str))
      reg_str.append("\n")

      # reg function
      reg_str.append("always@(posedge i_clk) begin\n" )
      reg_str.append("  if(i_rst_n == 1'd0) begin\n"  )
      reg_str.append("    %s <= %s;\n"                % (reg_name, default_name,))
      reg_str.append("  end\n"                        )
      reg_str.append("  else if(%s) begin\n"          % write_name)
      reg_str.append("    %s <= wdata & %s;\n"        % (reg_name, value_bit_name,))
      reg_str.append("  end\n"                        )
      reg_str.append("end\n"                          )
      reg_str.append("\n"                             )

      # output
      for bit in reg['bit']:
        output_name = "o_sw_%s" % bit[0]
        bit_range   = bit[1]
        bit_start   = bit_range[1]
        bit_end     = bit_range[0]
        bit_len     = bit_end - bit_start + 1

        if bit_len > 1:
          bit_range_str = "[%d:%d]" % (bit_end, bit_start)
        else:
          bit_range_str = "[%d]" % bit_start

        reg_str.append("assign %-16s = %s%s;\n" % (output_name, reg_name, bit_range_str,))
      reg_str.append("\n")

    # rdata
    bit_index  = 0
    rdata_list = []
    for bit in reg['bit']:
      bit_range   = bit[1]
      bit_start   = bit_range[1]
      bit_end     = bit_range[0]

      if bit_index != bit_start:
        rdata_list.append("%d'd0" % (bit_start - bit_index))
      if reg['type'] == "RW":
        rdata_list.append("o_sw_%s" % bit[0])
      else:
        rdata_list.append("i_ro_%s" % bit[0])
      bit_index = bit_end + 1
    if bit_index != 32:
      rdata_list.append("%d'd0" % (32 - bit_index))

    reg_str.append("assign %s = {\n" % rdata_name)
    reg_str.append("    " + "\n   ,".join(rdata_list[::-1]) + "\n")
    reg_str.append("};\n")

      
    reg_str.append("//==========================================================\n")
    reg_str.append("// %-44s   end/*}}}*/\n" % reg['name'])
    reg_str.append("//==========================================================\n")
    reg_str.append("\n")

  return reg_str
# ==========================================================
#  gen reg                                           end#}}}
# ==========================================================

# ==========================================================
#  gen rdata                                       start#{{{
# ==========================================================
def gen_rdata(data):
  rdata_str = []
  for reg in data:
    addr_name  = "%s_ADDR"    % reg['name'].upper()
    rdata_name = "rdata_%s_w" % reg['name']

    rdata_str.append("    %-22s : rdata <= %-21s;\n" % (addr_name, rdata_name,))
    
  return rdata_str
# ==========================================================
#  gen rdata                                         end#}}}
# ==========================================================

# ==========================================================
#  gen reg file                                    start#{{{
# ==========================================================
def gen_reg_file(
    mode_name,
    io_str,
    localparam_str,
    reg_wire_str,
    write_signal_str,
    reg_str,
    rdata_str,
    ):
  with open("../reg_file/%s_reg_cfg.v" % mode_name, "w") as f:
    f.write("module M%s_reg_cfg\n" % mode_name                              )
    f.write("(\n"                                                           )
    f.write("  // clk / rst_n\n"                                            )
    f.write("   input                    i_clk\n"                           )
    f.write("  ,input                    i_rst_n\n"                         )
    f.write("\n"                                                            )
    f.write("  // apb port\n"                                               )
    f.write("  ,input                    i_psel\n"                          )
    f.write("  ,input  [15:0]            i_paddr\n"                         )
    f.write("  ,input                    i_penable\n"                       )
    f.write("  ,input                    i_pwrite\n"                        )
    f.write("  ,input  [31:0]            i_pwdata\n"                        )
    f.write("  ,output [31:0]            o_prdata\n"                        )
    f.write("\n"                                                            )
    f.write("".join(io_str))
    f.write(");\n"                                                          )
    f.write("//==========================================================\n")
    f.write("// apb bus ctrl                                 start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("wire                        wr                             ;\n")
    f.write("wire                        rd                             ;\n")
    f.write("wire      [15:0]            addr                           ;\n")
    f.write("wire      [31:0]            wdata                          ;\n")
    f.write("reg       [31:0]            rdata                          ;\n")
    f.write("\n"                                                            )
    f.write("assign wr       = i_psel &  i_penable &  i_pwrite          ;\n")
    f.write("assign rd       = i_psel & ~i_penable & ~i_pwrite          ;\n")
    f.write("assign addr     = i_paddr                                  ;\n")
    f.write("assign wdata    = i_pwdata                                 ;\n")
    f.write("assign o_prdata = rdata                                    ;\n")
    f.write("//==========================================================\n")
    f.write("// apb bus ctrl                                   end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("//==========================================================\n")
    f.write("// localparam                                   start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("".join(localparam_str))
    f.write("//==========================================================\n")
    f.write("// localparam                                     end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("//==========================================================\n")
    f.write("// reg and wire                                 start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("".join(reg_wire_str))
    f.write("//==========================================================\n")
    f.write("// reg and wire                                   end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("//==========================================================\n")
    f.write("// write signal gen                             start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("".join(write_signal_str))
    f.write("//==========================================================\n")
    f.write("// write signal gen                               end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("".join(reg_str))
    f.write("//==========================================================\n")
    f.write("// rdata                                        start/*{{{*/\n")
    f.write("//==========================================================\n")
    f.write("always@(posedge i_clk) begin\n"                                )
    f.write("  if(i_rst_n == 1'd0) begin\n"                                 )
    f.write("    rdata <= 32'd0;\n"                                         )
    f.write("  end\n"                                                       )
    f.write("  else if(rd) begin\n"                                         )
    f.write("    case(addr)\n"                                              )
    f.write("".join(rdata_str))
    f.write("    default                : rdata <= 32'd0                ;\n")
    f.write("    endcase\n"                                                 )
    f.write("  end\n"                                                       )
    f.write("  else begin\n"                                                )
    f.write("    rdata <= rdata;\n"                                         )
    f.write("  end\n"                                                       )
    f.write("end\n"                                                         )
    f.write("//==========================================================\n")
    f.write("// rdata                                          end/*}}}*/\n")
    f.write("//==========================================================\n")
    f.write("\n"                                                            )
    f.write("endmodule\n"                                                   )

    f.close()
# ==========================================================
#  gen reg file                                      end#}}}
# ==========================================================

if __name__ == '__main__':
  if(len(sys.argv) < 2):
    print("not have input file")
    print("    %s reg_list" % sys.argv[0])
    sys.exit(0)
  file_name = sys.argv[1]

  mode_name = file_name.split("_")[0]

  data = get_reg_list(file_name)
  add_default(data)
  check_overlap(data)

  io_str           = gen_io_port(data)
  localparam_str   = gen_localparam(data)
  reg_wire_str     = gen_reg_wire(data)
  write_signal_str = gen_write_signal(data)
  reg_str          = gen_reg(data)
  rdata_str        = gen_rdata(data)

  gen_reg_file(
    mode_name,
    io_str,
    localparam_str,
    reg_wire_str,
    write_signal_str,
    reg_str,
    rdata_str,
  )

如果生成不了或是错误可能是下面几点
1.列表文件名不是<name>_reg_list
2.列表文件中列表名和文件不一致
3.代码中生成目录为 "../reg_file/<name>_reg_cfg.v",需要有相应的目录才能生成
4.是不是python版本问题


如果有问题或是有想探讨的可以加我微信:anyuexiu

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容

  • 组件 计算机是一种数据处理设备,它由CPU和内存以及外部设备组成。CPU负责数据处理,内存负责存储,外部设备负责数...
    哆啦灬少A梦阅读 1,590评论 1 2
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,746评论 0 10
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,813评论 0 27
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,855评论 0 5
  • 一个计数器通常是由一组触发器构成,该组触发器按照预先给定的顺序改变其状态,如果所有触发器的状态改变是在同一时钟脉冲...
    锦穗阅读 13,305评论 0 6