|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
引言
5 }% o# Q# @: e# }0 F, n前面我们介绍了流水线的写法(pipeline的写法),流水线是数字设计中很常用的一种设计方法,可以提高运行频率,提高吞吐量。
, y- o" `6 _" }! A$ ~+ X0 V" x# v( Z0 j6 G
如果组合逻辑延迟较大,一个时钟周期完成不了时,除了插入寄存器将组合逻辑拆分成流水线外,还可以采用multi-cycle的方式。: T# ~. `# \& x, B$ X
; y" X' O' Z) J$ p! {- `
multi-cycle的工作机制很简单,从给定输入之后,等待多个周期之后,再去采样输出结果。9 H$ L4 R7 @. n+ g; ^
; W9 O" L8 }0 b8 `本小节我们将通过一个小实验来说明multi-cycle的具体RTL实现。
% a3 U( U- g- s. V9 y4 P6 Z$ {3 k1 b& y j9 A' a
& a2 D. N& q# O: j
9 A9 }* e1 A, h' h4 ?% K
1,功能介绍
2 o) c6 d! \9 t" F2 ?假设有某个模块,其计算量很大,以致延迟较大,一个周期完成不了,需要3个cycle才行。/ N' K4 k8 Q% A) @; v# {4 A
( M4 i, j# \2 C) X* H假设时钟周期是10,这个模块的运算分为“加法,左移,减法”三个操作,分别用时7,8,9。& z* W8 D7 A) w; ?
: u9 b n, ?( Z/ r; h
: @% a, ]- W3 a2 ?/ Y( D" A5 }- `- c6 Y0 d1 F
2,RTL实现
* y" s. m" w, Z) r, ]2 P- \ n0 y; ] J/ S2 C* s( |' I3 {
如果在数字设计时,遇到上述模块描述的情况时,可以考虑multi-cycle实现。
. ?7 d+ R( V0 \9 v. e5 X# E6 t; C: W4 c# [
具体RTL如下:mc.v& Y) ?+ U. S- q
. \# g& Q2 M. z+ ?, p
其中关于状态机的写法,我们之前有专门介绍,如有疑问,请参考(你知道状态机的四种写法都是什么吗?)。* ~) r- M0 y+ `4 [1 P4 `
! Z9 U( x9 b# ~% Q
- /*
- * multi-cycle example
- * Rill 2015-05-29
- */
- 7 }: c& v0 R& W% o
- 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
- );
5 k# {7 ?2 h8 |: r2 |# K% t# e- //===================
- // control path, fsm
- //===================
- " w7 b7 ^ Z7 g# F9 j/ u2 s' L
- 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
- ' {" s5 b' x' f& ]2 d# H
7 o, W2 r4 F: b' l
' E; O3 R$ c t3,testbench
5 F9 L8 h3 X+ W/ S0 L& }' o
9 U: |, V! O# w$ A* X& W& y& {9 _具体multi-cycle模块是如何工作的呢,我们需要写个简单的TB验证一下:tb.v' @, K& X7 S# |* S/ Z
$ L3 {/ X& l" ?9 Q+ f- /*
- * 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
0 h2 h' N0 ]& X9 y/ h& i D; r2 A- O" v: l3 q
7 h# q* R3 T7 ]% g- {3 r7 m2 Y: o3 d
9 u8 q0 r% G& S/ v( B; ]* p: E
- L, Q) u1 c) o/ `% L4,nc脚本和vflist, E- i3 J! s& Q+ Z8 ]+ {
* i0 P8 E. C' ?* I( }2 p+ @: f7 o3 Z
mc.sh:
9 ~ E2 \1 r' Y% c/ f5 k0 k; \. Y8 I
- #! /bin/bash
- 8 s9 H. Z. V0 n- H( @
- $ F+ W' }- [2 j+ z! K) k
- #
- # mc.sh
- # usage: ./mc.sh c/w/r
- # Rill create 2014-09-03
- #
- 9 w; F" ?: U7 n- z- b
1 x T8 e/ x# ~$ r- TOP_MODULE=Ttb
/ V7 P' k1 r' v3 V- export SRC_DIR=$(pwd)
- tcl_file=run.tcl
) p8 t! ]2 }1 |# @9 F7 `- if [ $# != 1 ];then
- echo "args must be c/w/r"
- exit 0
- fi
- & k' C) d# c3 p! ]4 ^9 U/ G/ B B
- 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
% L' d5 R0 v+ W# J. ]% B
: j& {: Y' L, a4 U. K- if [ -e ${tcl_file} ];then
- rm ${tcl_file} -f
- fi
- touch ${tcl_file}
' \/ F, z* K, g0 l- 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
' `+ a) f' T" P# {5 B% a2 o- if [ $1 == "w" -o $1 == "r" ];then
- echo "sim start..."
- ncsim ${TOP_MODULE} -input ${tcl_file}
- fi
- 5 g' @' D! b1 K4 \% p' P
- echo "$(date) sim done!"
?8 C* M3 o& v 9 z( F) B+ R- x( ?3 y
; |& N- ]6 B; e- mvflist:
! S" I$ d) H+ F. o' Y
7 ^6 k4 @9 N+ D- -incdir ${SRC_DIR}
4 Z" L% G7 H) z: Q" C3 Q. ?# Z9 v- ${SRC_DIR}/mc.v
- ${SRC_DIR}/tb.v& H+ z K4 P x. }
% _: T0 _ Z2 V0 ?) g q! W$ I6 G0 _
$ `) b, i% b, p5 Z
5,验证4 Z, p# x, F& i& [7 n# }$ k# B
$ ]. n5 m; t* ] p
运行mc.sh c; mc.sh w即可得到仿真波形:
4 d8 j6 G" M- N) I6 M4 P, `+ Y7 k$ i4 K6 U) M7 N, R
: w3 k0 P1 f0 i" k! ?
{7 Z0 D+ H/ f8 f2 n1 z6 ]+ `' F/ M2 u; i/ N: P# D# |# {* s' z
通过波形可以看出,mc模块在经过3个cycle之后输出了运算结果3。$ s! @. y3 u! d/ Q% y, D+ Z
5 T& |! q; j" B
9 L+ }6 e) M" ~. V7 m9 A
f% }4 K( s6 D; I- \# L3 h, p6,小结' y8 }$ M" y) x: K
1 L3 \$ m8 V* K# S0 z
pipeline和multi-cycle是处理长延迟逻辑常用的两种方式,我们都介绍过了。
: ?' a0 ^6 ^/ I, e3 O+ C
& Y( f' { \: J$ G3 `( E6 d) hEnjoy!
9 ^6 r3 d/ O0 O- ]( w
5 N$ x; N4 f, Q7 a6 U, ` |
|