Chủ Nhật, 24 tháng 3, 2019

[Perl][Thiết kế lõi IP bằng script] Bài 7 - Script tạo RTL code của sub-module và top module

Tiếp theo bài 6, bài viết này tập trung mô tả giải thuật tạo RTL code cho các sub-module và top module. Đồng thời giải thích chi tiết cách thực hiện code Perl để cho các script tạo RTL code. Trước khi đọc bài viết này, các bạn cần đọc các phần trước để nắm chi tiết ví dụ được trình bày trong bài viết này.

1) Nguyên tắc cơ bản để tạo RTL code
Nguyên tắc cơ bản của việc tạo RTL code từ script là xác định phần code nào là cố định, phần code nào bị thay đổi theo cấu hình của người sử dụng.
  • Phần code cố định là phần code sẽ không bị thay đổi theo cấu hình người dùng. Dù thông số cấu hình thay đổi thì phần code này luôn giữ nguyên và luôn đúng.
  • Phần code thay đổi là phần biến thiên theo giá trị cấu hình. Để script Perl có thể tạo được phần code này thì một yếu tố quan trọng là bạn phải tạo được một mẫu RTL chung có thể dễ dàng được biến đổi bởi script theo thông số cấu hình.
Nhiều cách làm có thể được áp dụng tạo ra RTL code. Bài viết này giới thiệu một cách làm mà theo tác giả là dễ thực hiện và giúp dễ kiểm soát RTL code tạo ra theo từng phần.
  1. Bước 1: Tạo một file thư viện RTL mẫu. Chú ý, đây là file có nội dung cố định, không thay đổi. File này chứa 2 phần
    1. Phần RTL code cố định
    2. Phần "từ khóa" (keyword) tượng trưng cho "phần RTL code thay đổi". "Từ khóa" được đặt tên bất kỳ nhưng phải riêng biệt, không trùng lặp với bất kỳ các từ nào khác có trong fiel thư viện RTL mẫu.
  2. Bước 2: Tạo script để thực hiện các công việc sau
    1. Đọc file thư viện RTL mẫu
    2. Phát hiện các "từ khóa" đã ghi trong file thư viện
    3. Tạo RTL code tương ứng dựa trên các thông số cấu hình và thay thế đoạn RTL code đã tạo vào vị trí từ khóa có trong file thư viện
  3. Bước 3: In tất cả các dòng code cố định trong file thư viện và các dòng code tạo ở bước 2 vào một file RTL code hoàn chỉnh.
Hình 1: Các bước tạo một file RTL code từ script
2) Script tạo RTL code khối server (sub-module)
Áp dụng cách làm đã trình bày, chúng ta thực hiện script để tạo RTL code cho khối server.
Hình 2: Sơ đồ input/output của script tạo RTL code khối server
Khối server cần 2 file thư viện:
  • sfifo_lib.v là thư viện của FIFO đồng bộ lấy từ bài viết FIFO đồng bộ. Trong file thư viện này, chúng ta sẽ khai báo tất cả cac `define và parameter cần thiết để cấu hình SFIFO như đã phân tích ở bài 4.
`ifndef EMPTY_SIGNAL
  `define EMPTY_SIGNAL
`endif
`ifndef FULL_SIGNAL
  `define FULL_SIGNAL
`endif
parameter DATA_WIDTH    = 8;
parameter POINTER_WIDTH = 3;
  • server_lib.s là thư viện RTL code của server. RTL code của thư viện này như sau:
//===================================================================================
// File name: server.v
// Project:   RTL code generator
// Module:    server block
// Function: Receive the request of source and store them to FIFO
// Author:   nguyenquan.icd@gmail.com
// Website:   http://nguyenquanicd.blogspot.com
// License:   It's free but do not remove header when you use this RTL code for your project
//===================================================================================
module server (clk, rst_n, src_req, src_data, src_addr, sv_ack, sv_sel, sv_data, abt_ack);
  //
  //Start - generated by TOOL
  //$parameter_gen  
//End - generated by TOOL
  //
  parameter SEL_WIDTH  = 2**ADDR_WIDTH; //Number of destinations
  //Ports
  //
  input clk;           //Synchronous clock
  input rst_n;         //Asynchronous reset - active low
  input src_req;       //Source request - active high
  input [7:0] src_data;//Source data
  input [ADDR_WIDTH-1:0] src_addr; //Destination address
  input [SEL_WIDTH-1:0] abt_ack; //Acknowledgment from arbiter to server
  output wire sv_ack; //Acknowledgment from server to source
  output wire [SEL_WIDTH-1:0] sv_sel; //Select arbiter of destination
  output wire [7:0] sv_data; //Data from server to arbiter
  //
  //Internal signals
  //
  wire src_we;
  wire abt_re;
  wire sfifo_empty;
  wire sfifo_full;
  wire sfifo_not_full;
  wire sfifo_not_empty;
  wire [ADDR_WIDTH-1:0] sfifo_addr;
  //
  //Logic
  //
  //Data FIFO
sfifo #(.DATA_WIDTH(8), .POINTER_WIDTH(POINTER_WIDTH)) d_sfifo (
  .clk(clk),
  .rst_n(rst_n),
  .wr(src_we),
  .rd(abt_re),
  .data_in(src_data[7:0]),
  .sfifo_empty(sfifo_empty),
  .sfifo_full(sfifo_full),
  .data_out(sv_data[7:0])
  );
  //Address FIFO
sfifo #(.DATA_WIDTH(ADDR_WIDTH), .POINTER_WIDTH(POINTER_WIDTH)) a_sfifo (
  .clk(clk),
  .rst_n(rst_n),
  .wr(src_we),
  .rd(abt_re),
  .data_in(src_addr[ADDR_WIDTH-1:0]),
  .sfifo_empty(), //Float - no-used
  .sfifo_full(),  //Float - no-used
  .data_out(sfifo_addr[ADDR_WIDTH-1:0])
  );
  //SFIFO control
  //Write to FIFO
  assign src_we = src_req & sv_ack;
  //
  assign sfifo_not_full  = ~sfifo_full;
  assign sv_ack          = sfifo_not_full;
  //
  assign sfifo_not_empty = ~sfifo_empty;
  assign abt_re          = sfifo_not_empty & |abt_ack[SEL_WIDTH-1:0];
  //Address decoder
  //Start - this RTL code is generated by script
  //$address_decoder 
//End - this RTL code is generated by script

endmodule //server
Trong đoạn code trên, các bạn sẽ thấy đây là một mô tả RTL code cho module server với gần như đầy đủ các thành phần cần thiết theo thứ tự như đã phân tích ở phần 1 của bài 4. Ngoại trừ hai vị trí tô màu vàng:
  • $parameter_gen là một từ khóa mà script Perl sẽ phát hiện và thay thế bằng các dòng khai báo giá trị parameter cho khối server lấy từ file router_spec.xls
  • $address_decoder là một từ khóa mà script Perl sẽ phát hiện và thay thế bằng mạch giải mã địa chỉ. Mạch này tạo ra tín hiệu sv_sel có số lượng bit thay đổi.
Giải thuật của script gen_server_rtl.pltạo ra RTL code khối server như sau:
Hình 3: Giải thuật script tạo RTL code khối server
Nội dung script gen_server_rtl.pl tạo ra RTL của khối server như sau:
#!/usr/bin/perl
###############################################################################
# Project:   RTL code generator
# Function:   Gen SERVER module
# Author:   nguyenquan.icd@gmail.com
# Website:   http://nguyenquanicd.blogspot.com
###############################################################################
use warnings;
use strict;
use get_config; # execute ./get_config.pm $ARGV[0]
#
#Get the value from package get_config
#
my $addr_width = $get_config::addr_width;
my $ptr_width = $get_config::ptr_width;
my $dest_num = $get_config::dest_num;
#
#Generate RTL code
#
my $lib_dir   = "./verilog_lib";
my $input_lib = "$lib_dir/server_lib.v";
my $rtl_line;
#
print "--- Reading $input_lib to create the RTL\n";
#
open (LIBFILE, "$input_lib") || die ("Cannot open $input_lib because $!");
foreach my $line (<LIBFILE>) {
  chop($line); #Remove the end character of a line - it is the new line
  $line =~ s/\r//g; #Remove symbol "^M" of a MS-DOS text file
  #Generate parameters
  if ($line =~ /\$parameter_gen/) {
    $rtl_line .= "  parameter ADDR_WIDTH = $addr_width;\n";
    $rtl_line .= "  parameter POINTER_WIDTH = $ptr_width;\n";
  }
  #Generate ADDRESS DECODER
  elsif ($line =~ /\$address_decoder/) {
    for (my $dest_id = 0; $dest_id < $dest_num; $dest_id++) {
      $rtl_line .= "  assign sv_sel[$dest_id] = sfifo_not_empty & (sfifo_addr[ADDR_WIDTH-1:0] == $dest_id);\n";
    }
  }
  else {
    $rtl_line .= "$line\n";
  }
}
close LIBFILE;
#
#Put/Insert/Copy the RTL code of FIFO to RTL code of SERVER
#
$input_lib = "$lib_dir/sfifo_lib.v";
print "--- Reading $input_lib to create the RTL\n";
open (LIBFILE, "$input_lib") || die ("Cannot open $input_lib because $!");
foreach my $line (<LIBFILE>) {
  chop($line);
  $line =~ s/\r//g; #Remove symbol "^M" of a MS-DOS text file
  $rtl_line .= "$line\n";
}
close LIBFILE;
#
#Create SERVER module
#
my $out_dir   = "./verilog_rtl";
my $output_file = "$out_dir/server.v";
print "--- Creating $output_file\n";
#
open (OUTFILE, ">$output_file") || die ("Cannot open $output_file because $!");
print OUTFILE $rtl_line;
close OUTFILE;
print "--- COMPLETED creating $output_file\n";
#
__END__
Giải thích cơ bản về đoạn code Perl trên đây:
  • Lấy thông tin cấu hình từ file excel
use get_config; # execute ./get_config.pm $ARGV[0]
#
#Get the value from package get_config
#
my $addr_width = $get_config::addr_width;
my $ptr_width = $get_config::ptr_width;
my $dest_num = $get_config::dest_num;
  • Kiểm tra từ khóa $parameter_gen và tạo các khai báo parrameter
if ($line =~ /\$parameter_gen/) {
    $rtl_line .= "  parameter ADDR_WIDTH = $addr_width;\n";
    $rtl_line .= "  parameter POINTER_WIDTH = $ptr_width;\n";
  }
  • Kiểm tra từ khóa $address_decoder và tạo RTL code mạch giải mã địa chỉ
#Generate ADDRESS DECODER
  elsif ($line =~ /\$address_decoder/) {
    for (my $dest_id = 0; $dest_id < $dest_num; $dest_id++) {
      $rtl_line .= "  assign sv_sel[$dest_id] = sfifo_not_empty & (sfifo_addr[ADDR_WIDTH-1:0] == $dest_id);\n";
    }
  }
  • Copy file thư viện sfifo_lib.v 
$input_lib = "$lib_dir/sfifo_lib.v";
print "--- Reading $input_lib to create the RTL\n";
open (LIBFILE, "$input_lib") || die ("Cannot open $input_lib because $!");
foreach my $line (<LIBFILE>) {
  chop($line);
  $line =~ s/\r//g; #Remove symbol "^M" of a MS-DOS text file
  $rtl_line .= "$line\n";
}
close LIBFILE;
  • In RTL code vào file server.v
my $out_dir   = "./verilog_rtl";
my $output_file = "$out_dir/server.v";
print "--- Creating $output_file\n";
#
open (OUTFILE, ">$output_file") || die ("Cannot open $output_file because $!");
print OUTFILE $rtl_line;
close OUTFILE;
3) Script tạo RTL code khối arbiter (sub-module)
Vì cách thực hiện tương tự khối server nê tác giả chỉ trình bày giải thuật của script tạo RTL code khối arbiter, tên gen_arbiter_rtl.pl. Chi tiết code Perl các bạn có thể download về tham khảo.
Hình 4: Giải thuật của script tạo RTL code khối arbiter
4) Script tạo RTL code khối router (top module)
Khối router là top module kết nối các server arbiter theo số lượng đã cấu hình. Script tạo RTL code khối này (gen_router_rtl.pl) chủ yếu xử lý việc tạo ra các kết nối (connection) đúng và gọi các instance.
Hình 5: Giải thuật của script tạo RTL code module router (top module)
5) Script tổng của tool router_generator
Script tổng tên gen_all_rtl.pl đơn giản chỉ gọi và chạy 3 script gen_server_rtl.plgen_arbiter_rtl.pl gen_router_rtl.pl.
system "./gen_server_rtl.pl $ARGV[0]";
system "./gen_arbiter_rtl.pl $ARGV[0]";
system "./gen_router_rtl.pl $ARGV[0]";
Lệnh chạy của tool router_generator là:
gen_all_rtl.pl <tên file cấu hình>
Ví dụ:
gen_all_rtl.pl router_spec.xls
Kết thúc bài viết này, các bạn đã có một tool tạo RTL code tự động cho ví dụ minh họa. Các bạn có thể download toàn bộ source code ở link phía cuối bài viết. Chú ý, code được tạo ra chỉ mới được tác giả kiểm tra cú pháp (syntax) chứ chưa chạy mô phỏng kiểm tra chức năng.

Link tải toàn bộ source code (Chỉ kiểm tra syntax, chưa mô phỏng RTL code):
1. Source code: router_generator
2. Pass (nếu có): nguyenquanicd

0 bình luận:

Đăng nhận xét