标签:
前面我们介绍了流水线的写法(http://blog.csdn.net/rill_zhen/article/details/45980039),流水线是数字设计中很常用的一种设计方法,可以提高运行频率,提高吞吐量。
如果组合逻辑延迟较大,一个时钟周期完成不了时,除了插入寄存器将组合逻辑拆分成流水线外,还可以采用multi-cycle的方式。
multi-cycle的工作机制很简单,从给定输入之后,等待多个周期之后,再去采样输出结果。
本小节我们将通过一个小实验来说明multi-cycle的具体RTL实现。
假设有某个模块,其计算量很大,以致延迟较大,一个周期完成不了,需要3个cycle才行。
假设时钟周期是10,这个模块的运算分为“加法,左移,减法”三个操作,分别用时7,8,9。
如果在数字设计时,遇到上述模块描述的情况时,可以考虑multi-cycle实现。
具体RTL如下:mc.v
其中关于状态机的写法,我们之前有专门介绍,如有疑问,请参考(http://blog.csdn.net/rill_zhen/article/details/39585367)。
/* * multi-cycle example * Rill 2015-05-29 */ module Mmulti_cycle ( input clk, input rst_n, input en_i, input [7:0] data_i, output en_o, output [7:0] data_o, output idle ); //=================== // control path, fsm //=================== localparam S_IDLE = 4‘d0; localparam S_CYCLE1 = 4‘d1; localparam S_CYCLE2 = 4‘d2; localparam S_CYCLE3 = 4‘d3; reg [3:0] c_state_r; wire [3:0] n_state; wire state_changed; wire cs_idle = (c_state_r == S_IDLE); wire cs_cycle1 = (c_state_r == S_CYCLE1); wire cs_cycle2 = (c_state_r == S_CYCLE2); wire cs_cycle3 = (c_state_r == S_CYCLE3); wire ns_idle = cs_cycle3; wire ns_cycle1 = cs_idle & en_i; wire ns_cycle2 = cs_cycle1; wire ns_cycle3 = cs_cycle2; assign state_changed = ns_idle | ns_cycle1 | ns_cycle2 | ns_cycle3; assign n_state = ( {4{ns_idle}} & S_IDLE | {4{ns_cycle1}} & S_CYCLE1 | {4{ns_cycle2}} & S_CYCLE2 | {4{ns_cycle3}} & S_CYCLE3 ); always @(posedge clk) if(~rst_n) c_state_r <= S_IDLE; else c_state_r <= n_state; //================= // data path,calc //================= wire [7:0] data1; wire [7:0] data2; wire [7:0] data3; assign #7 data1 = data_i + 1‘b1; assign #8 data2 = data1 << 1‘b1; assign #9 data3 = data2 - 1‘b1; assign en_o = cs_cycle3; assign data_o = data3; assign idle = cs_idle; endmodule
具体multi-cycle模块是如何工作的呢,我们需要写个简单的TB验证一下:tb.v
/* * multi-cycle example * Rill 2015-05-29 */ module Ttb; reg clk; reg rst_n; reg en_i_r; reg [7:0] data_i_r; wire en_o; wire [7:0] data_o; wire idle; Mmulti_cycle mc0 ( .clk (clk), .rst_n (rst_n), .en_i (en_i_r), .data_i (data_i_r), .en_o (en_o), .data_o (data_o), .idle (idle) ); initial begin clk = 1‘b0; rst_n = 1‘b0; en_i_r = 1‘b0; data_i_r = 8‘b0; fork forever #5 clk = ~clk; join_none repeat(10) @(posedge clk); rst_n = 1‘b1; repeat(10) @(posedge clk); @(posedge clk); en_i_r = 1‘b1; data_i_r = 8‘h1; repeat(10) @(posedge clk); $finish(); end endmodule
mc.sh:
#! /bin/bash # # mc.sh # usage: ./mc.sh c/w/r # Rill create 2014-09-03 # TOP_MODULE=Ttb export SRC_DIR=$(pwd) tcl_file=run.tcl if [ $# != 1 ];then echo "args must be c/w/r" exit 0 fi if [ $1 == "c" ]; then echo "compile lib..." ncvlog -f ./vflist -sv -update -LINEDEBUG; #ncelab -delay_mode zero -access +rwc -timescale 1ns/10ps ${TOP_MODULE} ncelab -delay_mode distribute -access +rwc -timescale 1ns/10ps ${TOP_MODULE} exit 0 fi if [ -e ${tcl_file} ];then rm ${tcl_file} -f fi touch ${tcl_file} if [ $1 == "w" ];then echo "open wave..." echo "database -open waves -into waves.shm -default;" >> ${tcl_file} echo "probe -shm -variable -all -depth all;" >> ${tcl_file} echo "run" >> ${tcl_file} echo "exit" >> ${tcl_file} fi if [ $1 == "w" -o $1 == "r" ];then echo "sim start..." ncsim ${TOP_MODULE} -input ${tcl_file} fi echo "$(date) sim done!"
vflist:
-incdir ${SRC_DIR} ${SRC_DIR}/mc.v ${SRC_DIR}/tb.v
运行mc.sh c; mc.sh w即可得到仿真波形:
通过波形可以看出,mc模块在经过3个cycle之后输出了运算结果3。
pipeline和multi-cycle是处理长延迟逻辑常用的两种方式,我们都介绍过了。
Enjoy!
标签:
原文地址:http://blog.csdn.net/rill_zhen/article/details/46240487