数字集成电路设计-20-multi-cycle

妖精的绣舞 提交于 2019-12-04 17:55:04

引言

前面我们介绍了流水线的写法(http://blog.csdn.net/rill_zhen/article/details/45980039),流水线是数字设计中很常用的一种设计方法,可以提高运行频率,提高吞吐量。

如果组合逻辑延迟较大,一个时钟周期完成不了时,除了插入寄存器将组合逻辑拆分成流水线外,还可以采用multi-cycle的方式。

multi-cycle的工作机制很简单,从给定输入之后,等待多个周期之后,再去采样输出结果。

本小节我们将通过一个小实验来说明multi-cycle的具体RTL实现。


1,功能介绍

假设有某个模块,其计算量很大,以致延迟较大,一个周期完成不了,需要3个cycle才行。

假设时钟周期是10,这个模块的运算分为“加法,左移,减法”三个操作,分别用时7,8,9。


2,RTL实现

如果在数字设计时,遇到上述模块描述的情况时,可以考虑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


3,testbench

具体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

4,nc脚本和vflist


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


5,验证

运行mc.sh c; mc.sh w即可得到仿真波形:




通过波形可以看出,mc模块在经过3个cycle之后输出了运算结果3。


6,小结

pipeline和multi-cycle是处理长延迟逻辑常用的两种方式,我们都介绍过了。

Enjoy!



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!