# **Software/Hardware Co-design Homework**
---
**姓名:李彥霆
學號:B1095105
指導老師:林宏益**
---
# HW1_vivado usage (4-bit full adder )
## **1. 實驗目的**
將4-bit full adder verilog code於軟體實現
## **2.實驗原理**
* 因半加法器沒有考慮進位傳輸的問題,故無法執行兩組 2 位元以上的二進位數相加,即半加法器缺少一個進位輸入端,當兩個較低位元之二進位數相加後,若有進位產生時,進位輸入可作為傳送進位與上一位元相加之用。
* 若在半加法器上多加一個輸入變數,就便成了全加法器 (Full Adder),故全加法器有加數、被加數與進位輸入等 3 個輸入變數,分別標示為 x、y 與Ci ,而此 3 個二進位數相加後,亦會產生一個和 (Sum) 與可能的進位 (Carry) 等 2 個輸出變數,分別標示為 S 與Co 。

* 利用卡諾圖法對以上真值表進行邏輯化簡,可得簡化後之布林函數式為

* 最後將化簡所得之布林函數,使用邏輯閘來實現全加法器之邏輯電路,如下圖(a)所示。

> 註:上圖 (a) 與 (b) 皆可用來執行全法加器運算功能之邏輯電路圖,而這兩個電路之輸出 S皆為 S = x ⊕ y ⊕ Ci,而為使全加法器之邏輯電路圖有不同的表示方式,進位輸出Co 採用邏輯功能相同,但表示形式不同之布林函數式 ,即圖 (a) 之進位輸出,而圖 (b) 之進位輸出
* 欲對兩組 n 位元之二進位數相加,必須串接 n 個全加器才能完成。若欲將兩組 4 位元之二進位數,被加數 x = x4x3x2x1與加數 y = y4y3y2y1執行加法運算規則如下:

* 根據上面之運算式可知,並行加法電路之原理是將較低位元之加數與被加數相加所得之進位,直接傳送至較高位元之進位輸入端,再與較高位元之加數與被加數相加所得之進位,再直接傳送至更高位元之進位輸入端,依此類推,便可執行 n 個位元之加法運算,此種加法器稱漣波進位加法器 (Ripple Carry Adder),亦可稱為並行加法器 (Parallel Adder)。
## **3.實驗結果**
#### **GTKWave波形圖**

| u3 | u2 | u1 | u0 |
| --- | --- | --- | --- |
| 黃 | 橘 | 紅 | 綠 |
#### **Vivado波形圖**

#### **Vivado schematic圖**
> Synthesis前

> Synthesis後

## **4.實驗心得**
這次的作業是第一次運用vivado來跑模擬,過程有點陌生,但其實只要照著講義先在VScode上將RTL寫好、模擬一次,再照著老師的上課講義一步一步操作vivado就可以完成這次的作業了,最麻煩的其實是在下載軟體和前置作業的設定,花費的時間漫長許多,也因為下載失敗而重複了幾次。
## **5.參考文獻**
崑山科技大學開放式課程/►【電機工程系】邏輯設計(Logic Design) 《數位學習認證通過》
組合邏輯電路設計─算術運算電路(p.6~p.8)
(http://ocw.ksu.edu.tw/mod/resource/view.php?id=89)
# HW2_BCD to Decimal Decoder with Preset/Reset
## 1.實驗目的
實現BCD to Decimal Decoder(RTL、testbench)並驗證其正確性
> 
> 實驗所附真值表
## 2.實驗結果
### RTL
```
module decoder4_to_10( in,out, reset_n, preset);
input [3:0] in;
input reset_n;
input preset;
output [9:0] out;
reg [9:0] out;
always @( in or reset_n or preset)
begin
if ( reset_n)
begin
if (~preset)
begin
out=8'd0;
case (in)
4'b0000:
out=~10'b0000000001;
4'b0001:
out=~10'b0000000010;
4'b0010:
out=~10'b0000000100;
4'b0011:
out=~10'b0000001000;
4'b0100:
out=~10'b0000010000;
4'b0101:
out=~10'b0000100000;
4'b0110:
out=~10'b0001000000;
4'b0111:
out=~10'b0010000000;
4'b1000:
out=~10'b0100000000;
4'b1001:
out=~10'b1000000000;
default:
out=10'b1111111111;
endcase
end
else
out = 10'b0000001111;
end
else
out=0;
end
endmodule
```
### Testbench
```
module decoder_tb;
wire [9:0] out;
reg reset_n;
reg preset;
reg [3:0] in;
integer i;
integer j;
initial
begin
$dumpfile("decoder4_to_10.vcd" );
$dumpvars(0, s);
$monitor( " reset_n=%b,preset=%b in(DCBA)=%b, out=%b ", reset_n,preset, in, out);
for ( i=0; i<16; i=i+1)
begin
reset_n = 1;
preset = 0;
{in} = i;
#50;
end
for ( i=0; i<16; i=i+1)
begin
reset_n = 0;
preset = 1;
{in} = i;
#50;
end
for ( i=0; i<16; i=i+1)
begin
reset_n = 1;
preset = 1;
{in} = i;
#50;
end
#50 $finish;
end
decoder4_to_10 s(in,out, reset_n, preset);
endmodule
```
預先設定好input 0000~1111所對應之值,接著由reset_n、preset決定output
---
### Vivado波形圖
> 
> reset_n = 1, preset = 0
> 
> reset_n = 0, preset = X
> 
> reset_n = 1, preset = 1
### Vivado schematic圖

Synthesis前

Synthesis後
---
### I/O planning

---
### Floor planning

---
### On-Chip Power

---
### PYNQ-Z2
>
>
>
>reset_n = 0,preset=0,1
>
>reset_n=1,preset=1,out=1111000000
>
>reset_n=1,preset=0,in=0,out=0111111111
>
>reset_n=1,preset=0,in=1,out=1011111111
>
>reset_n=1,preset=0,in=2,out=1101111111
>
>reset_n=1,preset=0,in=3,out=1110111111
>
>reset_n=1,preset=0,in=4,out=1111011111
>
>reset_n=1,preset=0,in=5,out=1111101111
>
>reset_n=1,preset=0,in=6,out=1111110111
>
>reset_n=1,preset=0,in=7,out=111111011
>
>reset_n=1,preset=0,in=8,out=111111101
>
>reset_n=1,preset=0,in=9,out=111111110
>
>reset_n=1,preset=0,in=10~15,out=111111111
## 3.實驗討論
我寫的RTL是用case的方式去選擇輸出結果,跟生成出來的電路圖滿相近的,會有一個記憶體先儲存相對應的input/output,再由reset_n、preset決定輸出是否需要重整或預設;若是用算術邏輯的方式撰寫所生成出的電路圖則會跟題目所附的參考資料中的電路圖十分相似。
功率分析報告則可以發現占比最高的是在I/O部分,可能是因為我用case撰寫,若是將真值表的每一項輸出(out[0 ~ 9])改用算術邏輯(&、|、~)的方式撰寫,Logic的power可能就會提高不少。
## 4.實驗心得
這次的實驗我是從真值表下手,可以用case或是大量算術邏輯的方式撰寫,而我想到上學期在計算機組織的3 to 8 decoder作業,覺得可以試著從中改寫,在改寫I/O並因應多出的限制reset_n、preset而添加了一些if-else迴圈用以面對不同的輸出,模擬結果也如題目所附真值表相同~!
## 5.參考文獻
https://www.ti.com/lit/ds/sdls109/sdls109.pdf
# HW3&4_Decimal to 7-seg display with Reset
## 1.實驗目的
實現Decimal to 7-seg display with Reset(RTL、Testbench),並嘗試在工作站上進行編譯、驗證和查看波形。
>
>
> 實驗所附真值表
## 2.實驗結果
### RTL
```
module dec_7seg (Reset_n, Dec_input,a,b,c,d,e,f,g,dp);
input Reset_n;
input [3:0]Dec_input;
reg [7:0]seg_out;
output a,b,c,d,e,f,g,dp;
always@(Dec_input or Reset_n)
begin
if (Reset_n)
begin
case (Dec_input)
4'b0000:seg_out = 8'b00000011;
4'b0001:seg_out = 8'b10011111;
4'b0010:seg_out = 8'b00100101;
4'b0011:seg_out = 8'b00001101;
4'b0100:seg_out = 8'b10011001;
4'b0101:seg_out = 8'b01001001;
4'b0110:seg_out = 8'b01000001;
4'b0111:seg_out = 8'b00011111;
4'b1000:seg_out = 8'b00000001;
4'b1001:seg_out = 8'b00001001;
4'b1010:seg_out = 8'b00010001;
4'b1011:seg_out = 8'b11000001;
4'b1100:seg_out = 8'b11100101;
4'b1101:seg_out = 8'b10000101;
4'b1110:seg_out = 8'b01100001;
4'b1111:seg_out = 8'b01110001;
default:seg_out=8'b10010001;
endcase
end
else
seg_out=8'b10010001;
end
assign a = seg_out[7];
assign b = seg_out[6];
assign c = seg_out[5];
assign d = seg_out[4];
assign e = seg_out[3];
assign f = seg_out[2];
assign g = seg_out[1];
assign dp = seg_out[0];
endmodule
```
### Testbench
```
module dec_7seg_tb;
reg Reset_n;
reg [3:0] Dec_input;
reg [7:0] seg_out;
wire a,b,c,d,e,f,g,dp;
integer i;
initial
begin
$dumpfile("dec_7seg.vcd");
$dumpvars(0, s);
for ( i=0; i<16; i=i+1)
begin
Reset_n = 1;
{Dec_input} = i;
#50;
end
for ( i=0; i<16; i=i+1)
begin
Reset_n = 0;
{Dec_input} = i;
#50;
end
#50$finish;
end
dec_7seg s(Reset_n, Dec_input,a,b,c,d,e,f,g,dp);
endmodule
```
### Schematic
>
>Synthesis前
>
>Synthesis後
### On-Chip Power
>
### nWave
>
>Reset_n = 0
>
>Reset_n = 1
### PYNQ-Z2
>
>Reset_n=0,output=H
>
>Reset_n=1,Dec_input=0,output=0
>
>Reset_n=1,Dec_input=1,output=1
>
>Reset_n=1,Dec_input=2,output=2
>
>Reset_n=1,Dec_input=3,output=3
>
>Reset_n=1,Dec_input=4,output=4
>
>Reset_n=1,Dec_input=5,output=5
>
>Reset_n=1,Dec_input=6,output=6
>
>Reset_n=1,Dec_input=7,output=7
>
>Reset_n=1,Dec_input=8,output=8
>
>Reset_n=1,Dec_input=9,output=9
>
>Reset_n=1,Dec_input=10,output=A
>
>Reset_n=1,Dec_input=11,output=b
>
>Reset_n=1,Dec_input=12,output=c
>
>Reset_n=1,Dec_input=13,output=d
>
>Reset_n=1,Dec_input=14,output=E
>
>Reset_n=1,Dec_input=15,output=F
## 3.實驗心得
這次的作業,老師主要是想讓我們試著在工作站上使用各種EDA工具(VCS、Verdi)進行RTL的撰寫、模擬並觀看波形,而我在進行專題時就有使用過這些工具,因此這次作業也算是在幫我複習了一遍。
## 4.參考文獻
>漫談七段顯示器
>https://www.slideshare.net/ssuser1f4677/ss-78737038
# HW5_Improve code coverage of Design
## 1.實驗目的
>
1. 解釋為什麼FSM coverage只有75%。
2. 要如何提升 (A)Line, (B)FSM, ( C )Condition, (D)Branch "這四項"的coverage覆蓋率? 請證明。
>
>FSM狀態示意圖
## 2.實驗結果
### A.Line Coverage
經觀察,Line Coverage無法達到100%的原因是因為case中的default,在模擬時沒有跑到default那行,導致Line Coverage無法達到滿分。
>
### B.FSM Coverage
>
觀察講義所提供之test.v、test_bench.v所產生之波型圖發現,沒有S0->IDLE的情況發生,而依照本題FSM應該會有如下表格之幾種狀態發生。
| curr_state | next_state |
|:----------:|:----------:|
| IDLE | S0 |
| S0 | IDLE |
| S0 | S1 |
| S1 | IDLE |
本應要有四種狀態轉換卻少了一種的情況下,FSM coverage就會是(4-1)*100%/4 = 75%。
>
因為原test_bench中Delay的數量給到了#100(100個時間單位),等於Testbench Pattern的切換需要1ps*100=100ps,而由於Clock Cycle只有10ps因此在curr_state=S0時永遠吃不到input=0的訊號,所以少了S0->IDLE的轉換。
對此,我將切換pattern時的Delay縮小至#10(10個時間單位)
```
initial
begin
rst_n = 1'b0;
inp = 1'b0
#10 rst_n = 1'b1;inp = 1'b0
#10 inp=1'b1;
#10 inp=1'b0;
#10 inp=1'b1;
#10 inp=1'b1;
#10 inp=1'b1;
#10 inp=1'b0;
#10 inp=1'b1;
#10 inp=1'b0;
$finish;
end
```
>
>更改test_bench後的波型
### C.Condition Coverage
須將所有input測試一遍。
### D.Branch Coverage
Branch Coverage無法達到100%的原因如同Line Coverage,由於在default
導致Coverage無法達到100%。
>
## 3.實驗討論
### 解決方法
```
always @(curr_state)
begin
case(curr_state[1:0])
//IDLE:outp = 1'd0;
S0:outp = 1'd0;
S1:outp = 1'd1;
default:outp = 1'd0;
endcase
end
```
將IDLE的情況收進default,避免case只會在前三項(IDLE,S0,S1)執行而沒有執行到default。
>
>All coverage 100%
## 4.實驗心得
這次實驗不是自己做出一個成品,而是找出一個半成品的錯誤並想辦法除錯,我覺得非常有挑戰性,在做作業的同時需要重複跑模擬和開軟體來驗證方法是否可行,也在慢慢提升操作軟體的熟練度。
# HW6_7-Seg Display Controlled by UART
## 1.實驗目的

功能:
A.使用者輸入0~9, A~F, 七段顯示器顯示該數字/文字
B.使用者輸入其他數字/文字,七段顯示器顯示E
## 2.實驗原理
傳送訊息的方式

## 3.實驗過程
### RTL
**Top_module : rx_top.v**
```
`timescale 1ns / 1ps
module rx_top(
input clk,
input rst,
input rx_pin_in,
output [7:0] seg7
//output [7: 0] rx_data,
//output rx_done_sig
) ;
wire rx_pin_H2L;
H2L_detect rx_in_detect(
.clk( clk ),
.rst( rst ),
.pin_in( rx_pin_in ),
.sig_H2L( rx_pin_H2L )
);
wire rx_band_sig;
wire clk_bps;
rx_band_gen rx_band_gen(
.clk( clk ),
.rst( rst ),
.band_sig( rx_band_sig ),
.clk_bps( clk_bps )
);
wire [7:0] rx_data;
rx_ctl rx_ctl(
.clk( clk ),
.rst( rst ),
.rx_pin_in( rx_pin_in ),
.rx_pin_H2L( rx_pin_H2L ),
.rx_band_sig( rx_band_sig ),
.rx_clk_bps( clk_bps ),
.rx_data( rx_data ),
.rx_done_sig( rx_done_sig )
);
ASCII2seg7 u_ASCII2seg7(
.rst( rx_done_sig ),
.ASCII( rx_data ),
.seg7_out(seg7)
);
endmodule
```
**H2L Signal detect : H2L_detect.v**
```
`timescale 1ns / 1ps
module H2L_detect(
input clk,
input rst,
input pin_in,
output sig_H2L
);
reg pin_pre;
assign sig_H2L = !pin_in & pin_pre;
always @( posedge clk or posedge rst )
if( rst )
pin_pre <= 1'b0;
else
pin_pre <= pin_in;
endmodule
```
**Control : rx_ctl.v**
```
`timescale 1ns / 1ps
module rx_ctl(
input clk,
input rst,
input rx_pin_in,
input rx_pin_H2L,
output reg rx_band_sig,
input rx_clk_bps,
output reg[7:0] rx_data,
output reg rx_done_sig
) ;
localparam [3:0] IDLE = 4'd0, BEGIN = 4'd1, DATA0 = 4'd2,
DATA1 = 4'd3, DATA2 = 4'd4, DATA3 = 4'd5,
DATA4 = 4'd6, DATA5 = 4'd7, DATA6 = 4'd8,
DATA7 = 4'd9, END = 4'd10, BFREE = 4'd11;
reg [3:0] pos;
always@( posedge clk or posedge rst )
if( rst )
begin
rx_band_sig <= 1'b0;
rx_data <= 8'd0;
pos <= IDLE;
rx_done_sig <= 1'b0;
end
else
case( pos )
IDLE:
//等待start訊號H2L發生跳到BEGINrx_bandsig
//輸出1開始生rx_clk_bps訊
if( rx_pin_H2L)
begin
rx_band_sig <= 1'b1;
pos <= pos + 1'b1;
rx_data <= 8'd0;
end
BEGIN://用9600bps 速度檢查rxinput 為跳到 DATAO-DATAO
if( rx_clk_bps)
begin
if( rx_pin_in == 1'b0 )
begin
pos <= pos + 1'b1;
end
else
begin
rx_band_sig <= 1'b0;
pos <= IDLE;
end
end
DATA0,DATA1,DATA2,DATA3,DATA4,DATA5,DATA6,DATA7://儲存至rx_ata
if( rx_clk_bps)
begin
rx_data[ pos - DATA0 ] <= rx_pin_in;
pos <= pos + 1'b1;
end
END:
//完成接收rx_doneg_n
//?出0停止產生rx_clk_bps訊號,跳到BRE
if( rx_clk_bps)
begin
rx_done_sig <= 1'b1;
pos <= pos + 1'b1;
rx_band_sig <= 1'b0;
end
BFREE://rx_done_sig輪出到IDLE
begin
rx_done_sig <= 1'b0;
pos <= IDLE;
end
endcase
endmodule
```
**9600bps generator : rx_band_gen.v**
```
`timescale 1ns / 1ps
module rx_band_gen(
input clk,
input rst,
input band_sig,
output reg clk_bps
);
parameter SYS_RATE = 125000000;
parameter BAND_RATE = 9600;
parameter CNT_BAND = SYS_RATE / BAND_RATE;
parameter HALF_CNT_BAND = CNT_BAND / 2;
reg [13:0]cnt_bps;
always @( posedge clk or posedge rst )
if( rst )
begin
cnt_bps <= HALF_CNT_BAND;
clk_bps <= 1'b0;
end
else if( !band_sig )
begin
cnt_bps <= HALF_CNT_BAND;
clk_bps <= 1'b0;
end
else if( cnt_bps == CNT_BAND )
begin
cnt_bps <= 14'd0;
clk_bps <= 1'b1;
end
else
begin
cnt_bps <= cnt_bps + 1'b1;
clk_bps <= 1'b0;
end
endmodule
```
**ASCII_to_7Seg Signal : ascii2seg7.v**
```
module ASCII2seg7 (
rst,
ASCII,
seg7_out
);
input rst;
input [7:0] ASCII;
reg [7:0] seg7;
output [7:0] seg7_out;
always@( posedge rst )
begin
//if ( rst ) begin
case (ASCII)
8'b0011_0000:
seg7=8'b00000011;
8'b0011_0001:
seg7=8'b10011111;
8'b0011_0010:
seg7=8'b00100101;
8'b0011_0011:
seg7=8'b00001101;
8'b0011_0100:
seg7=8'b10011001;
8'b0011_0101:
seg7=8'b01001001;
8'b0011_0110:
seg7=8'b01000001;
8'b0011_0111:
seg7=8'b00011111;
8'b0011_1000:
seg7=8'b00000001;
8'b0011_1001:
seg7=8'b00001001;
8'b0100_0001:
seg7=8'b00010001;
8'b0100_0010:
seg7=8'b11000001;
8'b0100_0011:
seg7=8'b11100101;
8'b0100_0100:
seg7=8'b10000101;
8'b0100_0101:
seg7=8'b01100001;
8'b0100_0110:
seg7=8'b01110001;
default:
seg7=8'b01100001;
endcase
//end
//else seg7=7'b1111111;
end
assign seg7_out=seg7;
endmodule
```
### Testbench
```
`timescale 1ns / 1ps
module rx_top_tb();
reg clk;
reg rst;
reg uart_rx;
//wire [7:0] rx_data;
//wire rx_done_sig;
wire [7:0] seg7;
integer i;
reg [7:0] data_test=8'b0;
// 100MHz A9600bps
rx_top u_rx_top(
.clk(clk),
.rst(rst),
.rx_pin_in(uart_rx),
.seg7(seg7)
//.rx_data(rx_data),
//.rx_done_sig(rx_done_sig)
);
initial
begin
$dumpfile("rx_top.vcd");
$dumpvars;
end
initial
begin
clk=0;
for (i=0;i<256;i=i+1)
begin
rst=1;
#15 rst=0;
uart_rx=1;
//起始位
#260000 uart_rx=0;
//數據
#260000 uart_rx = data_test[0];
#260000 uart_rx = data_test[1];
#260000 uart_rx = data_test[2];
#260000 uart_rx = data_test[3];
#260000 uart_rx = data_test[4];
#260000 uart_rx = data_test[5];
#260000 uart_rx = data_test[6];
#260000 uart_rx = data_test[7];
//停止
#260000 uart_rx=1;
#260000 uart_rx=1;
#260000 uart_rx=1;
#260000 uart_rx=1;
#260000 uart_rx=1;
#260000 uart_rx=1;
#260000 uart_rx=1;
data_test = data_test + 1;
end
#50 $finish;
end
always #10 clk=~clk;
endmodule
```
## 4.實驗結果
### Schematic
>
>Elaborate design Layout
>
>synthesis Layout
### nWave
>
>ASCII code 30~39 (0~9)
>
>ASCII code 41~46 (A~F)
### Coverage
>
### Lint
>
### Modular Schematic
>
### FPGA(PYNQ-Z2)模擬
https://youtu.be/se_9PbGOfNc?si=PrSsT6LgN_ETMv2-
# HW7_人月神話讀書心得報告
---
A1095128邱奕綸
B1095105李彥霆
---
書中介紹出了人月的概念,並點出了一個與常識違背的事實---
「以人月來推估工作規模的大小是危險的,也是一個容易遭收誤解的迷思」
需要互相交流的工作,其完成並不會因為人數的增加而減少時間,反而會因為溝通與訓練的問題,導致完成時間比原排定的時程更加延後!
如書中所說---
「在一個時程已經落後的軟體專案中增加人手,只會讓他更落後」
軟體的專案管理耗費多少時間是由1.連續性的限制 2.可切分的子工作數量 決定的
所以良好的時程規劃才是一個好的專案管理應注意的重點
其中也提到了安排軟體專案的行程:
1/3 規劃
1/6 寫程式
1/4 組件測試與早期系統測試
1/4 系統測試,完成所有組件
其實實際上寫程式的時間只占了1/6,測試卻占了1/2,點出測試需要的時間是很長的!
許多專案安排的時間無法符合時程就是測試的部分安排的比例太小了。
書中也提到,在系統設計上,保有「**概念整體性(conceptual integrity)**」是非常重要的原則,也就是反映出同一組的設計理念。
而要達成概念整體性,要把工作拆分成架構(architecture)與實作(implementation)兩部分:
一、架構---由少數人來完成架構的設計,制定出功能、規格(specification),並站在使用者的利益上解決問題,以達成概念整體性,例如電路設計前,所需要遵照的Specification和手冊(UG)。
二、實作---根據架構的規定進行實作,建造出更快、更便宜的產品。且並非只有架構具有創意性,實作的設計同樣需要創意性,例如在產品的成本效能比(cost-performance ratio)就非常仰賴實作人員來提升,例如根據Spec的規定,使用不同的電路來設計出實作的成果。
在設計中,便利性(同時達到簡單且直接的要求之設計性)的好壞,與概念整體性是正相關的,如果不能與系統的基本概念相容,不如就把整個系統另起爐灶,可見概念整體性的重要。並且,把人力拆分成架構設計、實作和實現等垂直分工,如此的垂直分工不但大幅減輕了水平分工的負擔,結果也大幅簡化溝通,並增加了概念整體性。
在失敗為成功之母的章節也提到,第一次出爐的系統絕少是有用的,就專案管理而言,預先規劃一個本來就打算丟掉的實驗品是必須的,所以面對設計完成後仍需改變這項事實的第一步就是接受改變的事實---**「唯一不變的就是變」**。
在化整為零的章節中,也談到了Top-Down的設計方式,如同Verilog的撰寫方式,大型的專案管理也可以使用到Top-Down的方法設計,持續細分精緻的步驟,在這每個精緻過程中,就逐漸的界定出不同的模組(Module),使整體模組化,並且盡量使用High-Level的表示方式,細節則影藏起來,書中的這些原則都可以套用在電路設計上,如Verilog的模組化、以及High-Level的寫法增加合成的彈性。
而好的Top-Down Design可以避免許多錯誤,原因如下:
1.結構與表示方法清楚,所以更容易描述出精確的模組(Module)功能。
2.藉由割與明確定義出獨立模組的過程,更可以避免系統的錯誤。
3.影藏的細節使結構中的缺陷更容易突顯出來。
4.每一次細分精緻的步驟,都可以對該步驟的結果進行測試,也可以更精確的判斷出何時、或為什麼該壯士斷腕重新設計。
# 軟硬體協同設計期末報告_同步FIFO
---
**組員:A1095128邱奕綸 B1095105李彥霆
指導教授:林宏益教授**
---
[TOC]
## 一、FIFO介紹
**FIFO** 是 **First In, First Out** 的縮寫,是指一個 Queue 所使用擁有的特性──**先進入** Queue 的工作將**先被完成**,之後進來的則必須等候,概念如下圖所示,先到的先結帳,後來的最後結帳:

**<p class="text-center">圖一:FIFO示意圖**
## 二、電路架構
FIFO的電路參數主要有寬度(Width) &深度(Depth)
我們將其設定為: Width=8 (bits) Depth=8 (個儲存單位)
如下圖:

**<p class="text-center">圖二:FIFO設定大小**
整個電路的Ports主要有時脈訊號clk、重置訊號rstn,以及Read/Writr相關的訊號,如:Writr端有wr_en,wr_data以及fifo_full(表示FIFO已經被寫滿),Read端則是有rd_en,rd_data以及fifo_empty(表示FIFO已經被讀空),WR&RD腳位如下圖:

**<p class="text-center">圖三:FIFO_WR&RD_Ports**
一般的FIFO是利用 wr_ptr 和 rd_ptr 來判斷是空或是滿。
當寫Data時,rear(wr_ptr) 會加一,讀Data時front(rd_ptr)會加一。
一開始 rear = front時FIFO為empty
而當FIFO為full時rear也會等於 front
但這樣會造成fifo_full跟fifo_empty的判斷條件都相同,如下圖:

**<p class="text-center">圖四:fifo_full & fifo_empty狀況相同**
解決方法是將判斷條件改成 rear(wr_ptr)= (rear+1) % Depth
If(rear = front)則 為 fifo_full,
但缺點是沒有辦法將FIFO寫滿,例如:
我們的Depth深度為8,但由於這個限制最多就只能填到7個data,如下圖:

**<p class="text-center">圖五:最多填滿Depth-1個Data**
所以我們另外使用control訊號cnt來判斷電路為寫滿或是讀空,
寫入時cnt會加一,讀出時cnt會減一
若cnt為0則代表目前為讀空狀態,cnt為8為寫滿
cnt的Verilog code如下:
```
//counter support to decide full/empty
always @ (posedge clk or negedge rstn)
begin
if(!rstn)
cnt <= 0;
else if (wr_en && !rd_en && !fifo_full) //only write effective
cnt <= cnt + 1;
else if (!wr_en && rd_en && !fifo_empty) //only read effective
cnt <= cnt - 1;
else
cnt <= cnt;
end
```
## 三、同步FIFO設計&模擬
### RTL & TB
RTL code:
```
module syn_fifo(clk, rstn, wr_en, rd_en, wr_data, rd_data, fifo_full, fifo_empty);
parameter width = 8;
parameter depth = 8;
input clk;
input rstn;
input wr_en;
input rd_en;
input [width - 1 : 0] wr_data;
output reg [width - 1 : 0] rd_data;
//full_empty_decide
output fifo_full;
output fifo_empty;
//counter show that fifo's number
//$clog2(depth) = log 2 depth ~ decide how much bit is needed
reg [$clog2(depth): 0] cnt;
//write read pointer
reg [depth - 1 : 0] wr_ptr;
reg [depth - 1 : 0] rd_ptr;
reg [width - 1 : 0] fifo [depth - 1 : 0];
//write_ptr control
always @ (posedge clk or negedge rstn)
begin
if(!rstn)
wr_ptr <= 0;
else if(wr_en && (!rd_en) && (!fifo_full))
wr_ptr <= wr_ptr + 1;
else
wr_ptr <= wr_ptr;
end
//read_ptr control
always @ (posedge clk or negedge rstn)
begin
if(!rstn)
rd_ptr <= 0;
else if((rd_en) && (!wr_en) && (!fifo_empty))
rd_ptr <= rd_ptr + 1;
else
rd_ptr <= rd_ptr;
end
//data_write
integer i;
always @ (posedge clk or negedge rstn)
begin
if(!rstn)
begin //reset fifo
for(i = 0; i < depth; i = i + 1)
fifo[i] <= 0;
end
else if(wr_en && (!rd_en))
fifo[wr_ptr] <= wr_data;
else
fifo[wr_ptr] <= fifo[wr_ptr];
end
//data_read
always @ (posedge clk or negedge rstn)
begin
if(!rstn)
rd_data <= 0;
else if (rd_en && (!wr_en))
begin
rd_data <= fifo[rd_ptr];
fifo[rd_ptr] <= 0;
end
else
rd_data <= rd_data;
end
//counter support to decide full/empty
always @ (posedge clk or negedge rstn)
begin
if(!rstn)
cnt <= 0;
else if (wr_en && !rd_en && !fifo_full) //only write effective
cnt <= cnt + 1;
else if (!wr_en && rd_en && !fifo_empty) //only read effective
cnt <= cnt - 1;
else
cnt <= cnt;
end
assign fifo_full = (cnt == depth)? 1 : 0;
assign fifo_empty = (cnt == 0) ? 1 : 0;
endmodule
```
Testbench
```
module syn_fifo_tb;
reg clk, rstn;
reg wr_en, rd_en;
wire fifo_full, fifo_empty;
reg [7 : 0] wr_data;
wire [7 : 0] rd_data;
initial
begin
$dumpfile("wave.vcd");
$dumpvars(0,myfifo);
end
syn_fifo myfifo(
.clk(clk),
.rstn(rstn),
.wr_en(wr_en),
.rd_en(rd_en),
.fifo_full(fifo_full),
.fifo_empty(fifo_empty),
.wr_data(wr_data),
.rd_data(rd_data)
);
initial
begin
rstn = 1;
wr_en = 0;
rd_en = 0;
repeat(2) @(negedge clk);
rstn = 0;
@(negedge clk);
rstn = 1;
@(negedge clk);
wr_data = {$random}%60;
wr_en = 1;
repeat(2) @ (negedge clk);
wr_data = {$random}%60;
@(negedge clk);
wr_en = 0;
rd_en = 1;
repeat(4) @ (negedge clk);
rd_en = 0;
wr_en = 1;
wr_data = {$random}%60;
repeat(5) @ (negedge clk);
wr_data = {$random}%60;
repeat(2) @ (negedge clk);
wr_en = 0;
rd_en = 1;
repeat(2) @ (negedge clk);
rd_en = 0;
wr_en = 1;
wr_data = {$random}%60;
repeat(3) @ (negedge clk);
wr_en = 0;
repeat(2) @ (negedge clk);
rd_en = 1; //test error state
wr_en = 1;
wr_data = {$random}%60;
#50 $finish;
end
initial
begin
clk = 0;
forever
#5 clk = ~clk;
end
endmodule
```
### 利用Vivado進行模擬

**<p class="text-center">圖六:Elaborate Design**

**<p class="text-center">圖七:Synthesis Layout**
### Vivado波型模擬
1.一開始Reset結束後,wr_en經過了3次clock period,寫入三筆資料-08、08、 39,fifo_empty也由1變成0(不為空),如下圖:

**<p class="text-center">圖八:Write 3筆資料之波型**
2.rd_en經過了4次clock period,但因為原先只寫入三筆資料所以當要讀第四筆資料時輸出為00,而fifo_empty也在讀出第三筆料的同時由0轉為1(讀空)。

**<p class="text-center">圖九:Read資料至fifo_empty之波型**
3.wr_en經過了7次clock period , rd_en經過了2次clock period,wr_en再經過了3次clock period,cnt此時為7-2+3=8,此時fifo_full由0轉為1(寫滿)

**<p class="text-center">圖十:fifo_full由0轉為1(寫滿)**
wr_en、rd_en同時為1時,不做任何動作

**<p class="text-center">圖十一:wr_en,rd_ed同為1,不動作**
## 四、參考文獻
1.https://xilinx.eetrend.com/blog/2023/100571895.html
2.https://zhuanlan.zhihu.com/p/463030740?utm_id=0
3.https://www.infineon.com/dgdl/Infineon-AN1042_Understanding_Synchronous_FIFOs-ApplicationNotes-v07_00-CN.pdf?fileId=8ac78c8c7cdc391c017d071c535f21c3
4.賴志錦教授-資料結構Chap3. Stack & Queues