中國科學院大學數字集成電路作業開源——第6-8章
1、基礎概念問題
1.1 請簡要描述Mealy狀態機與Moore狀態機的特性及它們之間的聯系?
Moore狀態機:輸出僅僅與當前狀態有關;
Mealy狀態機:輸出不僅取決於當前狀態,還和輸入有關;
Mealy和Moore機之間可以相互轉化,對於每個Mealy機,都有一個等價的Moore機,Moore機狀態的上限為所對應的Mealy機狀態的數量和輸出數量的乘積。
1.2 請簡要描述SRAM的特點以及實際應用中與寄存器堆邏輯的優缺點比較?
SRAM的特點是工作速度快,只要電源不撤除,寫入SRAM的信息就不會消失,不需要刷新電路,同時在讀出時不破壞原來存放的信息,一經寫入可多次讀出,但集成度較低,功耗較大。
寄存器堆邏輯實現上更為簡單,但性能遠不如工藝優化后的SRAM。而SRAM性能雖強,但是實現上略微復雜。
1.3 請簡要描述門控時鍾常見電路結構以及適用范圍?
門控時鍾電路結構包括直接將控制信號與時鍾信號進行與操作(會產生毛刺,因此實際中很少使用)
基於鎖存器的時鍾門控方案(常用於ASIC設計)
基於觸發器的時鍾門控方案(常用於FPGA設計)
1.4 請簡要描述AHB總線與APB總線的基本特性以及在實際SoC系統中它們的適用場合?
AHB(Advanced High-performance Bus), 為高速總線,一般用來連接高速外設。APB (Advanced Peripheral Bus) 為低速總線,一般用來接低速外設。
在SoC系統中,AHB總線會掛載ARM處理器,RAM,DMA控制器等設備,通過AHB2APB Bridge與APB總線連接,APB上掛在UART,Timer等低速設備。
2 基於Verilog HDL進行邏輯電路設計
2.1平方根計算
設計一個時序邏輯電路,計算32位非負整數的平方根。對於輸入x,計算y = floor(sqrt(x)),即y是平方后不超過x的最大非負整數。例如:
l 輸入x = 256,輸出y = 16
l 輸入x = 255,輸出y = 15
l 輸入x = 2147483648,輸出y = 46340
l 輸入x = 4294967295,輸出y = 65535
頂層模塊名為sqrt_u32,輸入輸出功能定義:
名稱 | 方向 | 位寬 | 描述 |
---|---|---|---|
clk | I | 1 | 系統時鍾 |
rst_n | I | 1 | 系統異步復位,低電平有效 |
vld_in | I | 1 | 輸入數據有效指示 |
x | I | 32 | 輸入被開方數據 |
vld_out | O | 1 | 輸出數據有效指示 |
y | O | 16 | 輸出結果數據 |
設計要求:
l Verilog實現代碼可綜合,給出綜合以及仿真結果。
l 能夠處理多組數據輸入,且從數據輸入到結果輸出延遲周期數盡量少。
設計思路:
一開始想到的思路是cordic算法或者牛頓迭代法,測試后發現的問題是:對於32位數的輸入,cordic算法會存在不能收斂的問題(c語言測試,16次迭代,0-4的輸入可以精確的開方,更大的數可以通過歸一化然后處理,一直到256都能正確工作,但測試到32位的時候發現數據小的部分已經算不對了,如果非要用cordic的話就得寫很復雜的小數位數轉換邏輯了,所以我最后還是放棄了)牛頓迭代法中要用到除法,但我不想調用除法器ip,最后采用了逐次逼近法(其實可以理解成逆向的牛頓迭代)
逐次逼近算法流程如圖 所示,首先數據輸入data[7:0],接着設置實驗值D_z[3:0]和確定值D_q[3:0],然后按照從高往低的順序,依次將每一位置1(如D_z[3]置1),再將實驗值平方后與輸入數據比較,若實驗值的平方大於輸入值(D_z^2 > data),則此位為0(D_q[3]為0),反之(D_z^2 ≤ data)此位為1(D_q[3]為1);以此迭代到最后一位。
可見,如果是n bit的數據,那么需要n/2次迭代,每次計算如果一個周期,則需要n/2個周期。
借鑒:https://blog.csdn.net/qq_39507748/article/details/115468883
代碼實現:
module sqrt_u32 (
input clk,
input rst_n,
input vld_in,
input [31:0] x,
output reg vld_out,
output reg [16:0] y
);
parameter d_width = 32;
parameter q_width = 16;
reg [d_width-1:0] D [q_width:1];
reg [q_width-1:0] Q_z [q_width:1];
reg [q_width-1:0] Q_q [q_width:1];
reg valid_flag [q_width:1];
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
D[q_width] <= 0;
Q_z[q_width] <= 0;
Q_q[q_width] <= 0;
valid_flag[q_width] <= 0;
end
else if(vld_in)
begin
D[q_width] <= x;
Q_z[q_width] <= {1'b1,{(q_width-1){1'b0}}};
Q_q[q_width] <= 0;
valid_flag[q_width] <= 1;
end
else
begin
D[q_width] <= 0;
Q_z[q_width] <= 0;
Q_q[q_width] <= 0;
valid_flag[q_width] <= 0;
end
end
generate
genvar i;
for(i=q_width-1;i>=1;i=i-1)
begin:U
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
D[i] <= 0;
Q_z[i] <= 0;
Q_q[i] <= 0;
valid_flag[i] <= 0;
end
else if(valid_flag[i+1])
begin
if(Q_z[i+1]*Q_z[i+1] > D[i+1])
begin
Q_z[i] <= {Q_q[i+1][q_width-1:i],1'b1,{{i-1}{1'b0}}};
Q_q[i] <= Q_q[i+1];
end
else
begin
Q_z[i] <= {Q_z[i+1][q_width-1:i],1'b1,{{i-1}{1'b0}}};
Q_q[i] <= Q_z[i+1];
end
D[i] <= D[i+1];
valid_flag[i] <= 1;
end
else
begin
valid_flag[i] <= 0;
D[i] <= 0;
Q_q[i] <= 0;
Q_z[i] <= 0;
end
end
end
endgenerate
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
y <= 0;
vld_out <= 0;
end
else if(valid_flag[1])
begin
if(Q_z[1]*Q_z[1] > D[1])
begin
y <= Q_q[1];
vld_out <= 1;
end
else
begin
y <= {Q_q[1][q_width-1:1],Q_z[1][0]};
vld_out <= 1;
end
end
else
begin
y <= 0;
vld_out <= 0;
end
end
endmodule
testbench
module testbench ();
reg clk,rst_n,vld_in;
reg [31:0] x;
wire vld_out;
wire [15:0] y;
initial begin
clk <= 1'b0;
rst_n <= 1'b0;
vld_in <= 1'b0;
x <= 0;
#30
rst_n <= 1'b1;
vld_in <= 1'b1;
x <= 256;
#20
x <= 255;
#20
x <= 2147483648;
#20
x <= 4294967295;
end
always #10 clk <= ~clk;
sqrt_u32 u_sqrt_u32(
.clk(clk),
.rst_n(rst_n),
.vld_in(vld_in),
.x(x),
.vld_out(vld_out),
.y(y)
);
endmodule
仿真結果:
結果距離輸入延遲了16個周期
輸出值
輸入值
2.2數據排序
設計一個時序邏輯電路,對輸入32個8位無符號整數從小到大進行排序(若存在多個數據值相等,則不分先后,見例子)。例如:
l 輸入32個數據依次為:31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, 2, 2, 4, 4, 4, 4, 8, 16, 8, 16, 32, 32, 0, 10, 20, 30
l 輸出32個數據依次為:0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 7, 8, 8, 9, 10, 11, 13, 15, 16, 16, 17, 19, 20, 21, 23, 25, 27, 29, 30, 31, 32, 32
頂層模塊名為sort_32_u8,輸入輸出功能定義:
名稱 | 方向 | 位寬 | 描述 |
---|---|---|---|
clk | I | 1 | 系統時鍾 |
rst_n | I | 1 | 系統異步復位,低電平有效 |
vld_in | I | 1 | 輸入數據有效指示 |
din_0, din_1, … …, din_31 | I | 8 | 輸入數據0,輸入數據1,……, 輸入數據31 |
vld_out | O | 1 | 輸出數據有效指示 |
dout_0, dout_1, … …, dout_31 | O | 8 | 輸出數據0,輸出數據1,……, 輸出數據31 |
注:din_0 ~ din_31共32個輸入端口,dout_0~ dout_31共32個輸出端口。輸出數據dout_0 ~ dout_31的值從小到大排列。
設計要求:
l Verilog實現代碼可綜合,給出綜合以及仿真結果。
l 邏輯資源和延遲需要做權衡,使得數據輸入到結果輸出延遲周期數盡量少。
設計思路:
原先的思路是使用雙調排序,但雙調排序只能排單調增和單調減的,題目里面的有重復數的情況調不了。
采用冒泡排序,導致排序時間非常長,消耗資源比較少,不符合題目要求的延遲盡可能小。本來想再寫個全比較排序,但別的作業太多了,姑且算了,其實邏輯也挺簡單的。
冒泡實際上就是兩層循環反復比較,循環變量換成計數器就行了,使用了狀態機進行控制。
參考:http://www.manongjc.com/detail/19-ezdqhndvpyqmqjx.html
代碼實現:
module sort_32_u8 (
input clk,
input rst_n,
input vld_in,
input [7:0] din_0,din_1,din_2,din_3,din_4,din_5,din_6,din_7,din_8,din_9,din_10,din_11,din_12,din_13,din_14,din_15,din_16,din_17,din_18,din_19,din_20,din_21,din_22,din_23,din_24,din_25,din_26,din_27,din_28,din_29,din_30,din_31,
output reg vld_out,
output [7:0] dout_0,dout_1,dout_2,dout_3,dout_4,dout_5,dout_6,dout_7,dout_8,dout_9,dout_10,dout_11,dout_12,dout_13,dout_14,dout_15,dout_16,dout_17,dout_18,dout_19,dout_20,dout_21,dout_22,dout_23,dout_24,dout_25,dout_26,dout_27,dout_28,dout_29,dout_30,dout_31
);
parameter s_rst = 2'b00;
parameter s_load = 2'b01;
parameter s_sort = 2'b10;
parameter s_out = 2'b11;
reg [4:0] cnt_i,turn;
reg [7:0] data_fifo [31:0];
reg [1:0] cur_state,next_state;
reg reset,load_data,swap;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin
cur_state <= s_rst;
end
else begin
cur_state <= next_state;
end
end
always @(posedge clk) begin
if(reset == 1'b1) begin
data_fifo[0] <= 0;
data_fifo[1] <= 0;
data_fifo[2] <= 0;
data_fifo[3] <= 0;
data_fifo[4] <= 0;
data_fifo[5] <= 0;
data_fifo[6] <= 0;
data_fifo[7] <= 0;
data_fifo[8] <= 0;
data_fifo[9] <= 0;
data_fifo[10] <= 0;
data_fifo[11] <= 0;
data_fifo[12] <= 0;
data_fifo[13] <= 0;
data_fifo[14] <= 0;
data_fifo[15] <= 0;
data_fifo[16] <= 0;
data_fifo[17] <= 0;
data_fifo[18] <= 0;
data_fifo[19] <= 0;
data_fifo[20] <= 0;
data_fifo[21] <= 0;
data_fifo[22] <= 0;
data_fifo[23] <= 0;
data_fifo[24] <= 0;
data_fifo[25] <= 0;
data_fifo[26] <= 0;
data_fifo[27] <= 0;
data_fifo[28] <= 0;
data_fifo[29] <= 0;
data_fifo[30] <= 0;
data_fifo[31] <= 0;
turn <= 0;
cnt_i <= 0;
end
else if(load_data == 1'b1) begin
data_fifo[0] <= din_0;
data_fifo[1] <= din_1;
data_fifo[2] <= din_2;
data_fifo[3] <= din_3;
data_fifo[4] <= din_4;
data_fifo[5] <= din_5;
data_fifo[6] <= din_6;
data_fifo[7] <= din_7;
data_fifo[8] <= din_8;
data_fifo[9] <= din_9;
data_fifo[10] <= din_10;
data_fifo[11] <= din_11;
data_fifo[12] <= din_12;
data_fifo[13] <= din_13;
data_fifo[14] <= din_14;
data_fifo[15] <= din_15;
data_fifo[16] <= din_16;
data_fifo[17] <= din_17;
data_fifo[18] <= din_18;
data_fifo[19] <= din_19;
data_fifo[20] <= din_20;
data_fifo[21] <= din_21;
data_fifo[22] <= din_22;
data_fifo[23] <= din_23;
data_fifo[24] <= din_24;
data_fifo[25] <= din_25;
data_fifo[26] <= din_26;
data_fifo[27] <= din_27;
data_fifo[28] <= din_28;
data_fifo[29] <= din_29;
data_fifo[30] <= din_30;
data_fifo[31] <= din_31;
turn <= 31;
cnt_i <= 0;
end
else if(swap == 1'b1) begin
if(cnt_i < turn) begin
cnt_i <= cnt_i + 1;
if(data_fifo[cnt_i+1] < data_fifo[cnt_i]) begin
data_fifo[cnt_i+1] <= data_fifo[cnt_i];
data_fifo[cnt_i] <= data_fifo[cnt_i+1];
end
end
else begin
cnt_i <= 1;
turn <= turn - 1;
if(data_fifo[1] < data_fifo[0]) begin
data_fifo[1] <= data_fifo[0];
data_fifo[0] <= data_fifo[1];
end
end
end
end
always @(cnt_i,cur_state,turn,vld_in) begin
next_state <= s_rst;
case (cur_state)
s_rst : begin
reset <= 1'b1;
vld_out <= 1'b0;
next_state <= s_load;
end
s_load : begin
reset <= 1'b0;
if(vld_in == 1'b1) begin
load_data <= 1'b1;
vld_out <= 1'b0;
next_state <= s_sort;
end
else begin
next_state <= s_load;
end
end
s_sort : begin
swap <= 1'b1;
load_data <= 1'b0;
if(turn == 1 && cnt_i ==1 ) begin
next_state <= s_out;
vld_out <= 1'b1;
end
else begin
next_state <= s_sort;
end
end
s_out : begin
next_state <= s_load;
swap <= 1'b0;
end
default : begin
next_state <= s_rst;
end
endcase
end
assign dout_0 = data_fifo[0];
assign dout_1 = data_fifo[1];
assign dout_2 = data_fifo[2];
assign dout_3 = data_fifo[3];
assign dout_4 = data_fifo[4];
assign dout_5 = data_fifo[5];
assign dout_6 = data_fifo[6];
assign dout_7 = data_fifo[7];
assign dout_8 = data_fifo[8];
assign dout_9 = data_fifo[9];
assign dout_10 = data_fifo[10];
assign dout_11 = data_fifo[11];
assign dout_12 = data_fifo[12];
assign dout_13 = data_fifo[13];
assign dout_14 = data_fifo[14];
assign dout_15 = data_fifo[15];
assign dout_16 = data_fifo[16];
assign dout_17 = data_fifo[17];
assign dout_18 = data_fifo[18];
assign dout_19 = data_fifo[19];
assign dout_20 = data_fifo[20];
assign dout_21 = data_fifo[21];
assign dout_22 = data_fifo[22];
assign dout_23 = data_fifo[23];
assign dout_24 = data_fifo[24];
assign dout_25 = data_fifo[25];
assign dout_26 = data_fifo[26];
assign dout_27 = data_fifo[27];
assign dout_28 = data_fifo[28];
assign dout_29 = data_fifo[29];
assign dout_30 = data_fifo[30];
assign dout_31 = data_fifo[31];
endmodule
testbench
module testbench ();
reg clk,rst_n,vld_in;
reg [7:0] din_0,din_1,din_2,din_3,din_4,din_5,din_6,din_7,din_8,din_9,din_10,din_11,din_12,din_13,din_14,din_15,din_16,din_17,din_18,din_19,din_20,din_21,din_22,din_23,din_24,din_25,din_26,din_27,din_28,din_29,din_30,din_31;
wire vld_out;
wire [7:0] dout_0,dout_1,dout_2,dout_3,dout_4,dout_5,dout_6,dout_7,dout_8,dout_9,dout_10,dout_11,dout_12,dout_13,dout_14,dout_15,dout_16,dout_17,dout_18,dout_19,dout_20,dout_21,dout_22,dout_23,dout_24,dout_25,dout_26,dout_27,dout_28,dout_29,dout_30,dout_31;
initial begin
rst_n <= 1'b0;
clk <= 1'b0;
vld_in <= 1'b0;
din_0 <= 31;
din_1 <= 29;
din_2 <= 27;
din_3 <= 25;
din_4 <= 23;
din_5 <= 21;
din_6 <= 19;
din_7 <= 17;
din_8 <= 15;
din_9 <= 13;
din_10 <= 11;
din_11 <= 9;
din_12 <= 7;
din_13 <= 5;
din_14 <= 3;
din_15 <= 1;
din_16 <= 2;
din_17 <= 2;
din_18 <= 4;
din_19 <= 4;
din_20 <= 4;
din_21 <= 4;
din_22 <= 8;
din_23 <= 16;
din_24 <= 8;
din_25 <= 16;
din_26 <= 32;
din_27 <= 32;
din_28 <= 0;
din_29 <= 10;
din_30 <= 20;
din_31 <= 30;
#30
rst_n <= 1'b1;
vld_in <= 1'b1;
#20
vld_in <= 1'b0;
end
always #10 clk <= ~clk;
sort_32_u8 u_sort(
clk,
rst_n,
vld_in,
din_0,din_1,din_2,din_3,din_4,din_5,din_6,din_7,din_8,din_9,din_10,din_11,din_12,din_13,din_14,din_15,din_16,din_17,din_18,din_19,din_20,din_21,din_22,din_23,din_24,din_25,din_26,din_27,din_28,din_29,din_30,din_31,
vld_out,
dout_0,dout_1,dout_2,dout_3,dout_4,dout_5,dout_6,dout_7,dout_8,dout_9,dout_10,dout_11,dout_12,dout_13,dout_14,dout_15,dout_16,dout_17,dout_18,dout_19,dout_20,dout_21,dout_22,dout_23,dout_24,dout_25,dout_26,dout_27,dout_28,dout_29,dout_30,dout_31
);
endmodule
仿真結果:
花了將近500個周期排序(真的太慢了,我選算法的沒注意到要求延遲周期盡可能少)
dout_15到dout_31
dout_0到dout_19
2.3 矩陣掃描
如圖所示,ZigZag掃描就是將8x8的矩陣塊按照箭頭運動方向重新排列(從1開始到64結束):
設計一個時序邏輯電路,對輸入64個整數(按照行優先方式構成8x8的矩陣塊)按照ZigZag掃描方式依次輸出。例如:
l 輸入64個數據依次為:1, 2, 3, 4, ..., 61, 62, 63, 64
l 輸出64個數據依次為:1, 2, 9, 17, 10, 3, ..., 62, 55, 48, 56, 63, 64
頂層模塊名為mat_scan,輸入輸出功能定義:
名稱 | 方向 | 位寬 | 描述 |
---|---|---|---|
clk | I | 1 | 系統時鍾 |
rst_n | I | 1 | 系統異步復位,低電平有效 |
vld_in | I | 1 | 輸入數據有效指示 |
din | I | 10 | 輸入數據 |
vld_out | O | 1 | 輸出數據有效指示 |
dout | O | 10 | 輸出數據 |
注:每組輸入數據連續64個周期輸入,即vld_in連續64個時鍾周期有效;每組輸出數據連續64個周期輸出,即vld_out連續64個時鍾周期有效。
設計要求:
l Verilog實現代碼可綜合,給出綜合以及仿真結果。
l 使用SRAM緩存輸入數據,SRAM使用bit數盡量少。
l 從數據輸入到結果輸出延遲周期數盡量少。
設計思路:
用sram存64個數,輸入的時候地址順序增加,讀出的時候用狀態機實現zigzag要求的順序對應的地址變化。(我查了一下zigzag自動掃描的c語言實現,感覺在verilog里面實現太困難,所以就這樣暴力解決了)
代碼實現:
module mat_scan (
input clk,
input rst_n,
input vld_in,
input [9:0] din,
output reg vld_out,
output [9:0] dout
);
reg cs_n, w_en, r_en;
reg [5:0] addr;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
addr <= 6'b11_1111;
cs_n <= 1'b1;
w_en <= 1'b0;
r_en <= 1'b0;
vld_out <= 1'b0;
end
else if (vld_in) begin
w_en <= 1'b1;
cs_n <= 1'b0;
addr <= addr + 1'b1;
end
else begin
vld_out <= 1'b1;
r_en <= 1'b1;
case (addr)
6'b11_1111 : addr <= 6'b00_0000;
6'b00_0000 : addr <= 6'b00_0001;
6'b00_0001 : addr <= 6'b00_1000;
6'b00_1000 : addr <= 6'b01_0000;
6'b01_0000 : addr <= 6'b00_1001;
6'b00_1001 : addr <= 6'b00_0010;
6'b00_0010 : addr <= 6'b00_0011;
6'b00_0011 : addr <= 6'b00_1010;
6'b00_1010 : addr <= 6'b01_0001;
6'b01_0001 : addr <= 6'b01_1000;
6'b01_1000 : addr <= 6'b10_0000;
6'b10_0000 : addr <= 6'b01_1001;
6'b01_1001 : addr <= 6'b01_0010;
6'b01_0010 : addr <= 6'b00_1011;
6'b00_1011 : addr <= 6'b00_0100;
6'b00_0100 : addr <= 6'b00_0101;
6'b00_0101 : addr <= 6'b00_1100;
6'b00_1100 : addr <= 6'b01_0011;
6'b01_0011 : addr <= 6'b01_1010;
6'b01_1010 : addr <= 6'b10_0001;
6'b10_0001 : addr <= 6'b10_1000;
6'b10_1000 : addr <= 6'b11_0000;
6'b11_0000 : addr <= 6'b10_1001;
6'b10_1001 : addr <= 6'b10_0010;
6'b10_0010 : addr <= 6'b01_1011;
6'b01_1011 : addr <= 6'b01_0100;
6'b01_0100 : addr <= 6'b00_1101;
6'b00_1101 : addr <= 6'b00_0110;
6'b00_0110 : addr <= 6'b00_0111;
6'b00_0111 : addr <= 6'b00_1110;
6'b00_1110 : addr <= 6'b01_0101;
6'b01_0101 : addr <= 6'b01_1100;
6'b01_1100 : addr <= 6'b10_0011;
6'b10_0011 : addr <= 6'b10_1010;
6'b10_1010 : addr <= 6'b11_0001;
6'b11_0001 : addr <= 6'b11_1000;
6'b11_1000 : addr <= 6'b11_1001;
6'b11_1001 : addr <= 6'b11_0010;
6'b11_0010 : addr <= 6'b10_1011;
6'b10_1011 : addr <= 6'b10_0100;
6'b10_0100 : addr <= 6'b01_1101;
6'b01_1101 : addr <= 6'b01_0110;
6'b01_0110 : addr <= 6'b00_1111;
6'b00_1111 : addr <= 6'b01_0111;
6'b01_0111 : addr <= 6'b01_1110;
6'b01_1110 : addr <= 6'b10_0101;
6'b10_0101 : addr <= 6'b10_1100;
6'b10_1100 : addr <= 6'b11_0011;
6'b11_0011 : addr <= 6'b11_1010;
6'b11_1010 : addr <= 6'b11_1011;
6'b11_1011 : addr <= 6'b11_0100;
6'b11_0100 : addr <= 6'b10_1101;
6'b10_1101 : addr <= 6'b10_0110;
6'b10_0110 : addr <= 6'b01_1111;
6'b01_1111 : addr <= 6'b10_0111;
6'b10_0111 : addr <= 6'b10_1110;
6'b10_1110 : addr <= 6'b11_0101;
6'b11_0101 : addr <= 6'b11_1100;
6'b11_1100 : addr <= 6'b11_1101;
6'b11_1101 : addr <= 6'b11_0110;
6'b11_0110 : addr <= 6'b10_1111;
6'b10_1111 : addr <= 6'b11_0111;
6'b11_0111 : addr <= 6'b11_1110;
6'b11_1110 : addr <= 6'b11_1111;
endcase
end
end
sram #(
.ADDR_DEPTH(6),
.DATA_WIDTH(10),
.DATA_DEPTH(64)
) u_sram (
.clk(clk),
.rst_n(rst_n),
.cs_n(cs_n),
.w_en(w_en),
.r_en(r_en),
.addr(addr),
.din(din),
.dout(dout)
);
endmodule
sram
module sram #(
parameter ADDR_DEPTH = 4,
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 16
)(
input clk,
input rst_n,
input cs_n,
input w_en,
input r_en,
input [ADDR_DEPTH-1:0] addr,
input [DATA_WIDTH-1:0] din,
output reg [DATA_WIDTH-1:0] dout
);
reg [DATA_WIDTH-1:0] mem [DATA_DEPTH-1:0];
integer i;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
for (i = 0; i < DATA_DEPTH; i=i+1) begin
mem[i] <= (1'b0 << (DATA_WIDTH-1));
end
end
else if (w_en == 1'b1 && cs_n == 1'b0) begin
mem[addr] <= din;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
dout <= (1'b0 << (DATA_WIDTH-1));
end
else if (r_en == 1'b1 && cs_n == 1'b0) begin
dout <= mem[addr];
end
else begin
dout <= dout;
end
end
endmodule
testbench
module testbench ();
reg clk, rst_n, vld_in;
reg [9:0] din;
wire vld_out;
wire [9:0] dout;
initial begin
clk <= 1'b0;
rst_n <= 1'b0;
din <= 10'b00_0000_0000;
vld_in <= 1'b0;
#20
rst_n <= 1'b1;
vld_in <= 1'b1;
#1290
vld_in <= 1'b0;
end
always #10 clk <= ~clk;
always @(posedge clk) begin
if (vld_in == 1'b1) begin
din <= din + 1'b1;
end
end
mat_scan u_mat_scan(
.clk(clk),
.rst_n(rst_n),
.vld_in(vld_in),
.din(din),
.vld_out(vld_out),
.dout(dout)
);
endmodule
仿真結果:
先順序遞增的將1-64放到SRAM里面去
輸出的地址用狀態機實現,從而搞定zigzag掃描
2.4 AHB-SRAM控制器
設計一個基於AHB從接口的單端口SRAM控制器,實現SRAM存儲器與AHB總線的數據信息交換,將AHB總線上的讀寫操作轉換成標准SRAM讀寫操作。
SRAM大小為4096x32-bit,AHB接口數據大小固定為32-bit,AHB接口地址范圍為0x00000000 – 0x00003FFC。AHB接口能夠實現單次或突發模式的數據讀寫操作。
頂層模塊名為sram_ctr_ahb,輸入輸出功能定義:
名稱 | 方向 | 位寬 | 描述 |
---|---|---|---|
hclk | I | 1 | 系統時鍾 |
hresetn | I | 1 | 系統異步復位,低電平有效 |
hwrite | I | 1 | 寫有效 |
htrans | I | 2 | 當前傳輸類型 |
hsize | I | 3 | 當前傳輸大小 |
haddr | I | 32 | 讀寫地址 |
hburst | I | 3 | 當前突發類型 |
hwdata | I | 32 | 寫數據 |
hready | O | 1 | 傳輸完成指示 |
hresp | O | 2 | 傳輸響應 |
hrdata | O | 32 | 讀數據 |
sram_csn | O | 1 | SRAM片選,低電平有效 |
sram_wen | O | 1 | SRAM寫使能,低電平有效 |
sram_a | O | 12 | SRAM讀寫地址 |
sram_d | O | 32 | SRAM寫數據 |
sram_q | I | 32 | SRAM讀數據 |
注:仿真時SRAM時鍾與hclk相同,SRAM可以用FPGA的單端口SRAM IP核仿真模型或者使用單端口SRAM行為級模型代替。
設計要求:
設計要求:
l Verilog實現代碼可綜合,給出綜合以及仿真結果。
仿真時應給出各種典型情況下的數據讀寫接口信號波形。
設計思路:
作為實際設計並跑過完整ARM SoC的人,我覺得其實這里的輸入激勵應該由一個ARM CPU或者別的CPU來生成,但是沒有這個條件就只能手寫了。
在我做ARM SoC時,一個經驗之談是給ARM核寫代碼的時候(C語言),經常int_8/uint_8,int_16/uint_16或者int_32/int_32這些聲明了位寬的變量
例如當時寫的外設的寄存器表(基地址+偏移量)
所以很現實的就是存儲器的hsize要支持8位,16位和32位(更大的我寫的時候沒用到)
一個我想多數人意識不到的問題是CPU訪存是以字節(Byte)為單位的,即8bit。所以如果存儲器的數據寬度和總線寬度對齊,是32位的話,就必須得考慮訪存地址在haddr上左移的問題。如果直接是32位的訪存,那么haddr就必須左移兩位,16位的訪存就得左移一位,8位不用移位。或者就是直接用8位寬度的存儲器,如果要32位數的話就連續讀4個數出來放到總線上,16位數就連續讀2個數放到總線上。我用了前者(32位寬的存儲器)
代碼實現:
module sram_ctr_ahb (
input hclk,
input hresetn,
input hwrite,
input [1:0] htrans,
input [2:0] hsize,
input [31:0] haddr,
input [2:0] hburst,
input [31:0] hwdata,
output reg hready,
output reg [1:0] hresp,
output reg [31:0] hrdata,
output reg sram_csn,
output reg sram_wen,
output reg [11:0] sram_a,
output reg [31:0] sram_d,
input [31:0] sram_q
);
reg [31:0] hwdata_mask;
always @(posedge hclk or negedge hresetn) begin
if(hresetn == 1'b0) begin
hwdata_mask <= 0;
hready <= 1'b1;
hresp <= 2'b0;
hrdata <= 0;
sram_csn <= 1'b0;
sram_wen <= 1'b0;
sram_a <= 0;
sram_d <= 0;
end
else begin
sram_wen <= hwrite & htrans[1];
sram_a <= haddr[13:2];
end
end
always @(posedge hclk or negedge hresetn) begin
if(hresetn == 1'b0) begin
hrdata <= 0;
sram_d <= 0;
end
else begin
case (hsize[1:0])
2'b10: hwdata_mask <= 32'hFFFFFFFF; // Word write
2'b01: hwdata_mask <= (32'h0000FFFF << (16 * haddr[1])); // Halfword write
2'b00: hwdata_mask <= (32'h000000FF << (8 * haddr[1:0])); // Byte write
default: hwdata_mask <= 32'hFFFFFFFF;
endcase
sram_d <= (hwdata & hwdata_mask) | (hrdata & ~hwdata_mask);
hrdata <= sram_q;
end
end
endmodule
sram
module sram #(
parameter ADDR_DEPTH = 4,
parameter DATA_WIDTH = 8,
parameter DATA_DEPTH = 16
)(
input clk,
input rst_n,
input cs_n,
input w_en,
input [ADDR_DEPTH-1:0] addr,
input [DATA_WIDTH-1:0] din,
output reg [DATA_WIDTH-1:0] dout
);
reg [DATA_WIDTH-1:0] mem [DATA_DEPTH-1:0];
integer i;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
for (i = 0; i < DATA_DEPTH; i=i+1) begin
mem[i] <= (1'b0 << (DATA_WIDTH-1));
end
end
else if (w_en == 1'b1 && cs_n == 1'b0) begin
mem[addr] <= din;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
dout <= (1'b0 << (DATA_WIDTH-1));
end
else if (cs_n == 1'b0) begin
dout <= mem[addr];
end
end
endmodule
testbench
module testbench ();
reg hclk;
reg hresetn;
reg hwrite;
reg [1:0] htrans;
reg [2:0] hsize;
reg [31:0] haddr;
reg [2:0] hburst;
reg [31:0] hwdata;
wire hready;
wire [1:0] hresp;
wire [31:0] hrdata;
wire sram_csn;
wire sram_wen;
wire [11:0] sram_a;
wire [31:0] sram_d;
wire [31:0] sram_q;
initial begin
hclk <= 1'b0;
hresetn <= 1'b0;
hwrite <= 1'b0;
htrans <= 2'b00;
hsize <= 3'b000;
haddr <= 32'h00000000;
hburst <= 3'b000;
hwdata <= 32'h00000000;
#20
hresetn <= 1'b1;
hwrite <= 1'b1;
htrans <= 2'b10;
hsize <= 3'b010;
#20
haddr <= 32'hfffffffc;
hwdata <= 32'h20211212;
#20
hsize <= 3'b001;
haddr <= 32'hfffffffE;
hwdata <= 32'habcd0000;
#20
hsize <= 3'b000;
haddr <= 32'hffffffff;
hwdata <= 32'h59000000;
#20
hwrite <= 1'b0;
end
always #10 hclk <= ~hclk;
sram_ctr_ahb u_sram_ctrl_ahb(
.hclk(hclk),
.hresetn(hresetn),
.hwrite(hwrite),
.htrans(htrans),
.hsize(hsize),
.haddr(haddr),
.hburst(hburst),
.hwdata(hwdata),
.hready(hready),
.hresp(hresp),
.hrdata(hrdata),
.sram_csn(sram_csn),
.sram_wen(sram_wen),
.sram_a(sram_a),
.sram_d(sram_d),
.sram_q(sram_q)
);
sram #(
.ADDR_DEPTH(12),
.DATA_WIDTH(32),
.DATA_DEPTH(4096)
) u_sram (
.clk(hclk),
.rst_n(hresetn),
.cs_n(sram_csn),
.w_en(sram_wen),
.addr(sram_a),
.din(sram_d),
.dout(sram_q)
);
endmodule
仿真結果:
寫三個數(hwdata)(位寬不同),地址分別是fffffffc,fffffffe,ffffffff
單口sram,sram_wen取消后開始讀這三個數(hrdata)