|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
引言( w% U/ {4 z- s1 b% H# Q
前面我们介绍了流水线的写法(pipeline的写法),流水线是数字设计中很常用的一种设计方法,可以提高运行频率,提高吞吐量。4 d* l1 c4 ^6 j, |
7 X; W9 ? M( T& ]6 I" c% h- f如果组合逻辑延迟较大,一个时钟周期完成不了时,除了插入寄存器将组合逻辑拆分成流水线外,还可以采用multi-cycle的方式。
6 t _4 x" R% R7 d# W7 i5 ?# E* ?
1 R7 |- E# q' k, T+ G: A* ymulti-cycle的工作机制很简单,从给定输入之后,等待多个周期之后,再去采样输出结果。
$ {% f7 u$ j r8 _ |/ r' @6 ?
本小节我们将通过一个小实验来说明multi-cycle的具体RTL实现。
/ |$ v6 B8 f: ~ X! `+ Y9 ^6 K Y- V% A7 C5 }& K3 t; X
, Q* P- V* {$ R9 d% e
. } t: g" [$ d0 i
1,功能介绍
% C8 ?) l; B. g6 }+ r假设有某个模块,其计算量很大,以致延迟较大,一个周期完成不了,需要3个cycle才行。
5 Y P6 ]" p8 l. \' I5 X( U7 s5 Q. i9 R& @) N
假设时钟周期是10,这个模块的运算分为“加法,左移,减法”三个操作,分别用时7,8,9。
4 m/ p4 Z' K; D& C
1 ], z& }, [3 o" ]3 t% |( k/ P9 G! Z# t% J/ m
' I' X, h, {( S4 H( F$ C9 u- \* e
2,RTL实现6 z: m# l$ Y8 a0 l3 A, L- c6 N: l, d
5 c6 F0 ] R4 K4 U: ~- ~
如果在数字设计时,遇到上述模块描述的情况时,可以考虑multi-cycle实现。
" E7 @+ |# U' }' E( d
2 o5 I/ R0 _* S" Z- z, p具体RTL如下:mc.v
1 }' T1 M3 Y" M! G0 l8 O. k/ F0 O- E2 Y' l6 W' y6 C4 Z, c
其中关于状态机的写法,我们之前有专门介绍,如有疑问,请参考(你知道状态机的四种写法都是什么吗?)。
9 \2 s3 w0 d- x% R' ~( z2 z% i6 ~6 k$ f: F7 J
- /*
- * multi-cycle example
- * Rill 2015-05-29
- */
/ l) ?) ], |7 o3 H4 _" t- U- 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
- );
- 6 V! K) p4 S. O: ]/ a- ]6 o
- //===================
- // control path, fsm
- //===================
- ' X) J7 F9 b1 a6 p6 b4 x0 P) a1 i
- 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
. Y5 V6 X$ |) z/ ~% D
* O) k; E- c7 a* x- T) Q. F( p L7 P5 ?: O. ~$ w [
3,testbench
; Y* o( S1 r# E6 A8 w6 O( ? }4 \; c% W, E* q% |" |6 E
具体multi-cycle模块是如何工作的呢,我们需要写个简单的TB验证一下:tb.v1 x( l7 i5 g$ n+ I" z7 ~& s2 A* f: r
6 r: z4 m6 ?. g; B6 x0 u9 {
- /*
- * 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
- endmodule3 R) I& R1 P! ]/ ]% H7 s
7 C# I4 z+ s D/ P5 S$ R, x+ Z
0 I8 ]" z6 _1 F4 ?9 M* q
) E* N( w- x& P/ z' t- r( S/ `! d* @- x" X
4,nc脚本和vflist7 S! y# U0 o0 h" N9 B
& @" u& V+ L/ }' W( W( ~4 Umc.sh:1 r! P+ }" y+ U: d
7 A$ a& j1 K: Q n- #! /bin/bash
- ) G1 p8 T! s4 }! o& U$ H) r
- . R$ Z: j; x$ ?4 k, t7 t$ a
- #
- # mc.sh
- # usage: ./mc.sh c/w/r
- # Rill create 2014-09-03
- #
# U) }- T) j/ S& f7 i( ]9 ^$ o* r$ I
6 b8 \" _7 N( M* a- TOP_MODULE=Ttb
- 2 |9 \. b8 b. z3 P
- export SRC_DIR=$(pwd)
- tcl_file=run.tcl
i+ [ V' t1 K- if [ $# != 1 ];then
- echo "args must be c/w/r"
- exit 0
- fi
1 y! M4 q$ ?# z" b: ]& f6 m- 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
. _, v. D$ L& f% C# q- o2 r0 y0 O% F3 G E
! u0 S s+ ~1 G* H- if [ -e ${tcl_file} ];then
- rm ${tcl_file} -f
- fi
- touch ${tcl_file}
' F- ~3 K( B2 r) ~" s- 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
- + M' Q( u- s( j) q1 R' a
- if [ $1 == "w" -o $1 == "r" ];then
- echo "sim start..."
- ncsim ${TOP_MODULE} -input ${tcl_file}
- fi
- ' c7 b7 j9 A7 ]) Y0 S+ s
- echo "$(date) sim done!"
; D/ M# N$ ^: M6 `, P
& N. k( l& g4 |7 q
9 c U% y( M+ Q& Z, C6 e" cvflist:4 U9 \0 {& F6 Z2 ~: s$ j3 @# Z
0 S" | l; s3 m1 a
- -incdir ${SRC_DIR}
- 6 K* j, J) R/ ^6 Z: i8 U
- ${SRC_DIR}/mc.v
- ${SRC_DIR}/tb.v
- R; S& P {6 D! R3 h5 ?9 @6 C- A
% P" u [/ t' s0 t& D# B! d" M# M8 v
6 K0 ]! r2 r3 \2 S5,验证2 Z% v3 G. I' B: `1 H& h8 N
& G+ [) ^3 U. x& X1 D
运行mc.sh c; mc.sh w即可得到仿真波形:, {9 [/ b9 d# M r' q5 c5 _# r
% k1 x) F: X+ A$ U/ n# [* u
; w# o7 D4 O( @6 V/ H9 b, W
2 [: `: Z3 r* c/ v/ n6 a
# K9 u2 Y! T0 B4 C$ N1 r通过波形可以看出,mc模块在经过3个cycle之后输出了运算结果3。
6 Q: H# Z: y. }& i+ }- C
. E2 o6 x6 `" @1 r# g/ T
: D8 U5 r: q- C; z2 Y$ j* U5 a, C$ X0 Z# Z1 D" {
6,小结2 \6 O: [, O }" h
( ^" F! W; ^% z6 C6 j* m0 x# Kpipeline和multi-cycle是处理长延迟逻辑常用的两种方式,我们都介绍过了。
# M% {& [3 F" W3 ~- P& M1 r+ J$ |' x9 X, w, Z
Enjoy!4 F1 V9 C! n2 J' W# ?
+ n1 @0 v/ Y* U/ _; \1 v |
|