コーディング2
- LoadDataとStoreDataモジュールの記述を見て頂くと分かるように、最初のカウンター生成部分は同じにしています。
- LoadDataのパイプラインにはポイント数(Radix)を流していますが、固定ならこの必要はありません。タスクごとにポイント数が変化してもいいよう将来的な観点で記述[1]しています。
- 逆変換に関しては、これらのモジュールに影響しません。ただし、DCT等の前処理が加わると影響することがあります[2]。
コード(LoadData RTL)
/* **************************** MODULE PREAMBLE ********************************
Copyright (c) 2012, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
*/
// ***************************** MODULE HEADER *********************************
module fftLoadData (
iVld, iStall, iRadix,
oVld, oStall, oFlip,
mStrb, mAck, mData,
ram0WE, ram1WE, ram2WE, ram3WE,
ram0WA, ram1WA, ram2WA, ram3WA,
ram0WD, ram1WD, ram2WD, ram3WD,
reset, clk
);
// ************************* PARAMETER DECLARATIONS ****************************
// 最大のポイント数の指数はカウンタ等のビット範囲を定める
parameter MRR = 10; // Max Radix Radix
// *************************** I/O DECLARATIONS ********************************
// Pipe Input
// iRadixはiVldに同期していれば、タスクごとに値の変更が可能
input iVld;
output iStall;
input [3:0] iRadix;
// Pipe Output
// oVldは処理の終了を伝達するために存在
output oVld;
input oStall;
output [1:0] oFlip;
// Memory Interface
// メモリI/Fに接続してもよいし、データを供給するパイプにつなげてもよい
output mStrb;
input mAck;
input [127:0] mData;
// SRAMはトップモジュールで定義
// SRAM
output ram0WE, ram1WE, ram2WE, ram3WE;
output [MRR-3:0] ram0WA, ram1WA, ram2WA, ram3WA;
output [31:0] ram0WD, ram1WD, ram2WD, ram3WD;
// Utility
input reset;
input clk;
// **************************** LOCAL DECLARATIONS *****************************
// Counter
// Radix-4は4つ同時に処理するので、カウンタもN/4個数えればよい
reg [MRR-3:0] cnt, iCnt;
wire fin, iFin;
// Pipeline (Valid Control)
// パイプラインは0,1,2の3段、SuffixはパイプラインStageを示す
reg vld_0, vld_1, vld_2;
reg fin_0, fin_1, fin_2;
reg [MRR-3:0] cnt_0, cnt_1;
reg [3:0] radix_0, radix_1;
// この信号(p)は分岐・結合のため別途定義している
wire vld_0p;
wire stall_0p;
// Pipeline (Stall Control)
wire stall_s, stall_0, stall_1, stall_2;
// Pipeline (Data)
reg [127:0] data_1, data_2;
reg [MRR-1:0] addr0_2, addr1_2, addr2_2, addr3_2;
// SRAM Flip
reg flip;
// ******************************** MODULE BODY ********************************
// -----------------------------------------------------------------------------
// Control
// 最終カウントがパイプラインへ投入した後Stallを解放
// 終端からStallが伝搬しているので注意(トップモジュールのFIFOでタイミングアークは切断)
assign iStall = iVld & (!iFin | stall_s);
assign iFin = fin;
// -----------------------------------------------------------------------------
// Counter
// iCntのデフォルト設定はポイント数2^10、MRRを増やす場合は隙間(10,12,14,,,)を積み増して行く
// case()文でiRadix依存しない記述方法があればそれを採用すべき
always @(posedge clk)
if (reset)
cnt <= #1 {MRR-2{1'b0}};
else if (!stall_s)
cnt <= #1 fin
? {MRR-2{1'b0}}
: cnt + {{MRR-3{1'b0}}, iVld};
always @(
iRadix
)
case (iRadix)
4'h4: iCnt = {{MRR-4{1'b0}}, 2'h3};
4'h6: iCnt = {{MRR-6{1'b0}}, 4'hf};
4'h8: iCnt = {{MRR-8{1'b0}}, 6'h3f};
default:
iCnt = {MRR-2{1'b1}};
endcase
assign fin = (cnt == iCnt);
// -----------------------------------------------------------------------------
// Pipeline (Valid Control)
// 3段パイプラインのうち、vld_0とvld_1の間にデータパイプが合流
always @(posedge clk)
if (reset)
{vld_0, fin_0, radix_0, cnt_0}
<= #1 {MRR+4{1'b0}};
else if (!stall_s)
{vld_0, fin_0, radix_0, cnt_0}
<= #1 {iVld, iFin, iRadix, cnt};
always @(posedge clk)
if (reset)
{vld_1, fin_1, radix_1, cnt_1}
<= #1 {MRR+4{1'b0}};
else if (!stall_0p)
{vld_1, fin_1, radix_1, cnt_1}
<= #1 {vld_0p, fin_0, radix_0, cnt_0};
always @(posedge clk)
if (reset)
{vld_2, fin_2} <= #1 2'b00;
else if (!stall_1)
{vld_2, fin_2} <= #1 {vld_1, fin_1};
// 左辺と右辺の制御信号の合計が3を確認(3つのパイプの結合)、以下stall_0p, mStrbも同じ
assign vld_0p = vld_0 & mAck;
// -----------------------------------------------------------------------------
// Pipeline (Stall Control)
// バッファ型のパイプライン記述
// パイプラインの最終出力oVldは終了を示すfin_2信号が結合しているので、stall_2も同様の処理を施す
assign stall_s = vld_0 & stall_0;
assign stall_0 = stall_0p | !mAck;
assign stall_0p = vld_1 & stall_1;
assign stall_1 = vld_2 & stall_2;
assign stall_2 = oStall & fin_2;
// -----------------------------------------------------------------------------
// Pipeline (Data)
// データバッファ1段目、バス幅が異なればサイジング回路を挿入(64bitバス→2回のAckで1回のパイプ結合)
always @(posedge clk)
if (mStrb & mAck)
data_1 <= #1 mData;
// データバッファ2段目、無駄に思えるが実はここにフォーマット変換を挟む余地を作るため
always @(posedge clk)
if (!stall_1)
data_2 <= #1 data_1;
// データアドレスに対するBit Reverseを4ポートに対して実施
always @(posedge clk)
if (!stall_1) begin
addr0_2 <= #1 reverseFunc({cnt_1, 2'h0}, radix_1);
addr1_2 <= #1 reverseFunc({cnt_1, 2'h1}, radix_1);
addr2_2 <= #1 reverseFunc({cnt_1, 2'h2}, radix_1);
addr3_2 <= #1 reverseFunc({cnt_1, 2'h3}, radix_1);
end
// -----------------------------------------------------------------------------
// Output
assign oVld = vld_2 & fin_2;
// パイプライン準備が整っていなければ入力データを拒否
assign mStrb = vld_0 & !stall_0p;
// -----------------------------------------------------------------------------
// SRAM Flip
// FFTの実行の度にFlipすることで、使用するSRAMセットを選択する
// また、SRAMを使用する状態(vld=1)を組み合わせて出力(トップモジュールはこの信号でデータをブレンド)
always @(posedge clk)
if (reset)
flip <= #1 1'b0;
else if (oVld & !oStall)
flip <= #1 ~flip;
assign oFlip = {1'b0, vld_2 & !stall_2} << flip;
// -----------------------------------------------------------------------------
// SRAM Write
// SRAMにStallを効かす、また未使用時はWE(Write Enable)をActiveにしないことで低消費電力化を考慮する
assign ram0WE = vld_2 & !stall_2;
assign ram1WE = vld_2 & !stall_2;
assign ram2WE = vld_2 & !stall_2;
assign ram3WE = vld_2 & !stall_2;
// 4つのSRAMアドレスLSB2ビットは必ず排他的になり、これによりSRAM Bankに配分する
assign ram0WA = addr0_2[MRR-1:2] & {MRR-2{addr0_2[1:0] == 2'h0}}
| addr1_2[MRR-1:2] & {MRR-2{addr1_2[1:0] == 2'h0}}
| addr2_2[MRR-1:2] & {MRR-2{addr2_2[1:0] == 2'h0}}
| addr3_2[MRR-1:2] & {MRR-2{addr3_2[1:0] == 2'h0}};
assign ram1WA = addr0_2[MRR-1:2] & {MRR-2{addr0_2[1:0] == 2'h1}}
| addr1_2[MRR-1:2] & {MRR-2{addr1_2[1:0] == 2'h1}}
| addr2_2[MRR-1:2] & {MRR-2{addr2_2[1:0] == 2'h1}}
| addr3_2[MRR-1:2] & {MRR-2{addr3_2[1:0] == 2'h1}};
assign ram2WA = addr0_2[MRR-1:2] & {MRR-2{addr0_2[1:0] == 2'h2}}
| addr1_2[MRR-1:2] & {MRR-2{addr1_2[1:0] == 2'h2}}
| addr2_2[MRR-1:2] & {MRR-2{addr2_2[1:0] == 2'h2}}
| addr3_2[MRR-1:2] & {MRR-2{addr3_2[1:0] == 2'h2}};
assign ram3WA = addr0_2[MRR-1:2] & {MRR-2{addr0_2[1:0] == 2'h3}}
| addr1_2[MRR-1:2] & {MRR-2{addr1_2[1:0] == 2'h3}}
| addr2_2[MRR-1:2] & {MRR-2{addr2_2[1:0] == 2'h3}}
| addr3_2[MRR-1:2] & {MRR-2{addr3_2[1:0] == 2'h3}};
assign ram0WD = data_2[31:0] & {32{addr0_2[1:0] == 2'h0}}
| data_2[63:32] & {32{addr1_2[1:0] == 2'h0}}
| data_2[95:64] & {32{addr2_2[1:0] == 2'h0}}
| data_2[127:96] & {32{addr3_2[1:0] == 2'h0}};
assign ram1WD = data_2[31:0] & {32{addr0_2[1:0] == 2'h1}}
| data_2[63:32] & {32{addr1_2[1:0] == 2'h1}}
| data_2[95:64] & {32{addr2_2[1:0] == 2'h1}}
| data_2[127:96] & {32{addr3_2[1:0] == 2'h1}};
assign ram2WD = data_2[31:0] & {32{addr0_2[1:0] == 2'h2}}
| data_2[63:32] & {32{addr1_2[1:0] == 2'h2}}
| data_2[95:64] & {32{addr2_2[1:0] == 2'h2}}
| data_2[127:96] & {32{addr3_2[1:0] == 2'h2}};
assign ram3WD = data_2[31:0] & {32{addr0_2[1:0] == 2'h3}}
| data_2[63:32] & {32{addr1_2[1:0] == 2'h3}}
| data_2[95:64] & {32{addr2_2[1:0] == 2'h3}}
| data_2[127:96] & {32{addr3_2[1:0] == 2'h3}};
// **************************** FUNCTIONS and TASKS ****************************
// Bit ReverseとSRAM Bank配分のためのアドレス攪乱を行う
function [MRR-1:0] reverseFunc;
input [MRR-1:0] cnt;
input [3:0] radix;
reg [MRR-1:0] result;
reg [MRR-1:0] twid;
begin
// Reverse
// Radixの範囲でLSBとMSBが対象になるようビット配置を交換する
// Radixが増える場合はcase()の条件も増やす
case (radix)
4'h4: result = {{MRR-4{1'b0}},
cnt[0], cnt[1], cnt[2], cnt[3]
};
4'h6: result = {{MRR-6{1'b0}},
cnt[0], cnt[1], cnt[2], cnt[3],
cnt[4], cnt[5]
};
4'h8: result = {{MRR-8{1'b0}},
cnt[0], cnt[1], cnt[2], cnt[3],
cnt[4], cnt[5], cnt[6], cnt[7]
};
default:
result = {
cnt[0], cnt[1], cnt[2], cnt[3],
cnt[4], cnt[5], cnt[6], cnt[7],
cnt[8], cnt[9]
};
endcase
// 上位2ビットをLSB2ビットに排他的論理和を行いアドレス攪乱を行う
// この攪乱はBfCalcの初段の入力時に引き継がれる
// Twiddle Factor
case (radix)
4'h4: twid = {{MRR-2{1'b0}}, result[3:2]};
4'h6: twid = {{MRR-2{1'b0}}, result[5:4]};
4'h8: twid = {{MRR-2{1'b0}}, result[7:6]};
default:
twid = {{MRR-2{1'b0}}, result[MRR-1:MRR-2]};
endcase
// Result
reverseFunc = result ^ twid;
end
endfunction
endmodule // loadData
// *****************************************************************************
コード(StoreData RTL)
/* **************************** MODULE PREAMBLE ********************************
Copyright (c) 2012, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
*/
// ***************************** MODULE HEADER *********************************
module fftStoreData (
iVld, iStall, iRadix,
oVld, oStall, oFlip,
mStrb, mAck, mData,
ram0RE, ram1RE, ram2RE, ram3RE,
ram0RA, ram1RA, ram2RA, ram3RA,
ram0RD, ram1RD, ram2RD, ram3RD,
reset, clk
);
// ************************* PARAMETER DECLARATIONS ****************************
// 最大のポイント数の指数はカウンタ等のビット範囲を定める
parameter MRR = 10; // Max Radix Radix
// *************************** I/O DECLARATIONS ********************************
// Pipe Input
// iRadixはiVldに同期していれば、タスクごとに値の変更が可能
input iVld;
output iStall;
input [3:0] iRadix;
// Pipe Output
// oVldは処理の終了を伝達するために存在
output oVld;
input oStall;
output [1:0] oFlip;
// Memory Interface
// メモリI/Fに接続してもよいし、データを受け取るパイプにつなげてもよい
output mStrb;
input mAck;
output [127:0] mData;
// SRAM
// SRAMはトップモジュールで定義
output ram0RE, ram1RE, ram2RE, ram3RE;
output [MRR-3:0] ram0RA, ram1RA, ram2RA, ram3RA;
input [31:0] ram0RD, ram1RD, ram2RD, ram3RD;
// Utility
input reset;
input clk;
// **************************** LOCAL DECLARATIONS *****************************
// Counter
// Radix-4は4つ同時に処理するので、カウンタもN/4個数えればよい
reg [MRR-3:0] cnt, iCnt;
wire fin, iFin;
// Pipeline (Valid Control)
// パイプラインは0,1,2の3段、SuffixはパイプラインStageを示す
reg vld_0, vld_1, vld_2;
reg fin_0, fin_1, fin_2;
// この信号(p)は分岐・結合のため別途定義している
wire vld_2p;
wire stall_2p;
// Pipeline (Stall Control)
wire stall_s, stall_0, stall_1, stall_2;
// Pipeline (Data)
reg [MRR-1:0] addr0_0, addr1_0, addr2_0, addr3_0;
reg [1:0] addr0_1, addr1_1, addr2_1, addr3_1;
reg [127:0] data_2;
// SRAM Flip
reg flip;
// ******************************** MODULE BODY ********************************
// -----------------------------------------------------------------------------
// Control
// 最終カウントがパイプラインへ投入した後Stallを解放
// 終端からStallが伝搬しているので注意(トップモジュールのFIFOでタイミングアークは切断)
assign iStall = iVld & (!iFin | stall_s);
assign iFin = fin;
// -----------------------------------------------------------------------------
// Counter
// iCntのデフォルト設定はポイント数2^10、MRRを増やす場合は隙間(10,12,14,,,)を積み増して行く
// case()文でiRadix依存しない記述方法があればそれを採用すべき
always @(posedge clk)
if (reset)
cnt <= #1 {MRR-2{1'b0}};
else if (!stall_s)
cnt <= #1 fin
? {MRR-2{1'b0}}
: cnt + {{MRR-3{1'b0}}, iVld};
always @(
iRadix
)
case (iRadix)
4'h4: iCnt = {{MRR-4{1'b0}}, 2'h3};
4'h6: iCnt = {{MRR-6{1'b0}}, 4'hf};
4'h8: iCnt = {{MRR-8{1'b0}}, 6'h3f};
default:
iCnt = {MRR-2{1'b1}};
endcase
assign fin = (cnt == iCnt);
// -----------------------------------------------------------------------------
// Pipeline (Valid Control)
// 3段パイプラインのうち、vld_2と出力の間にデータパイプが分岐
always @(posedge clk)
if (reset)
{vld_0, fin_0} <= #1 2'b00;
else if (!stall_s)
{vld_0, fin_0} <= #1 {iVld, iFin};
always @(posedge clk)
if (reset)
{vld_1, fin_1} <= #1 2'b00;
else if (!stall_0)
{vld_1, fin_1} <= #1 {vld_0, fin_0};
always @(posedge clk)
if (reset)
{vld_2, fin_2} <= #1 2'b00;
else if (!stall_1)
{vld_2, fin_2} <= #1 {vld_1, fin_1};
assign vld_2p = vld_2 & mAck;
// -----------------------------------------------------------------------------
// Pipeline (Stall Control)
// バッファ型のパイプライン記述
// パイプラインの最終出力oVldは終了を示すfin_2信号が結合しているので、stall_2も同様の処理を施す
assign stall_s = vld_0 & stall_0;
assign stall_0 = vld_1 & stall_1;
assign stall_1 = vld_2 & stall_2;
assign stall_2 = stall_2p | !mAck;
assign stall_2p = oStall & fin_2;
// -----------------------------------------------------------------------------
// Pipeline (Data)
// データアドレスに対するアドレス攪乱を4ポートに対して実施
always @(posedge clk)
if (!stall_s) begin
addr0_0 <= #1 twidFunc({cnt, 2'h0}, iRadix);
addr1_0 <= #1 twidFunc({cnt, 2'h1}, iRadix);
addr2_0 <= #1 twidFunc({cnt, 2'h2}, iRadix);
addr3_0 <= #1 twidFunc({cnt, 2'h3}, iRadix);
end
always @(posedge clk)
if (!stall_0) begin
addr0_1 <= #1 addr0_0[1:0];
addr1_1 <= #1 addr1_0[1:0];
addr2_1 <= #1 addr2_0[1:0];
addr3_1 <= #1 addr3_0[1:0];
end
// Readデータを受け取るタイミングはREアサート(vld_0)から2サイクル後(vld_2)
always @(posedge clk)
if (!stall_1) begin
data_2[127:96] <= #1 ram0RD & {32{addr3_1 == 2'h0}} |
ram1RD & {32{addr3_1 == 2'h1}} |
ram2RD & {32{addr3_1 == 2'h2}} |
ram3RD & {32{addr3_1 == 2'h3}};
data_2[95:64] <= #1 ram0RD & {32{addr2_1 == 2'h0}} |
ram1RD & {32{addr2_1 == 2'h1}} |
ram2RD & {32{addr2_1 == 2'h2}} |
ram3RD & {32{addr2_1 == 2'h3}};
data_2[63:32] <= #1 ram0RD & {32{addr1_1 == 2'h0}} |
ram1RD & {32{addr1_1 == 2'h1}} |
ram2RD & {32{addr1_1 == 2'h2}} |
ram3RD & {32{addr1_1 == 2'h3}};
data_2[31:0] <= #1 ram0RD & {32{addr0_1 == 2'h0}} |
ram1RD & {32{addr0_1 == 2'h1}} |
ram2RD & {32{addr0_1 == 2'h2}} |
ram3RD & {32{addr0_1 == 2'h3}};
end
// -----------------------------------------------------------------------------
// Output
assign oVld = vld_2p & fin_2;
// パイプライン準備が整っていなければ出力データなしをアサート
assign mStrb = vld_2 & !stall_2p;
assign mData = data_2;
// -----------------------------------------------------------------------------
// SRAM Flip
// FFTの実行の度にFlipすることで、使用するSRAMセットを選択する
// また、SRAMを使用する状態(vld=1)を組み合わせて出力(トップモジュールはこの信号でデータをブレンド)
always @(posedge clk)
if (reset)
flip <= #1 1'b0;
else if (oVld & !oStall)
flip <= #1 ~flip;
assign oFlip = {1'b0, vld_0 & !stall_0} << flip;
// -----------------------------------------------------------------------------
// SRAM
// SRAMにStallを効かす、また未使用時はRE(Read Enable)をActiveにしないことで低消費電力化を考慮する
assign ram0RE = vld_0 & !stall_0;
assign ram1RE = vld_0 & !stall_0;
assign ram2RE = vld_0 & !stall_0;
assign ram3RE = vld_0 & !stall_0;
assign ram0RA = {
addr0_0[MRR-1:2] & {8{addr0_0[1:0] == 2'h0}} |
addr1_0[MRR-1:2] & {8{addr1_0[1:0] == 2'h0}} |
addr2_0[MRR-1:2] & {8{addr2_0[1:0] == 2'h0}} |
addr3_0[MRR-1:2] & {8{addr3_0[1:0] == 2'h0}}
};
assign ram1RA = {
addr0_0[MRR-1:2] & {8{addr0_0[1:0] == 2'h1}} |
addr1_0[MRR-1:2] & {8{addr1_0[1:0] == 2'h1}} |
addr2_0[MRR-1:2] & {8{addr2_0[1:0] == 2'h1}} |
addr3_0[MRR-1:2] & {8{addr3_0[1:0] == 2'h1}}
};
assign ram2RA = {
addr0_0[MRR-1:2] & {8{addr0_0[1:0] == 2'h2}} |
addr1_0[MRR-1:2] & {8{addr1_0[1:0] == 2'h2}} |
addr2_0[MRR-1:2] & {8{addr2_0[1:0] == 2'h2}} |
addr3_0[MRR-1:2] & {8{addr3_0[1:0] == 2'h2}}
};
assign ram3RA = {
addr0_0[MRR-1:2] & {8{addr0_0[1:0] == 2'h3}} |
addr1_0[MRR-1:2] & {8{addr1_0[1:0] == 2'h3}} |
addr2_0[MRR-1:2] & {8{addr2_0[1:0] == 2'h3}} |
addr3_0[MRR-1:2] & {8{addr3_0[1:0] == 2'h3}}
};
// **************************** FUNCTIONS and TASKS ****************************
// 上位2ビットをLSB2ビットに排他的論理和を行いアドレス攪乱を行う
// この攪乱はBfCalcの最終段の出力時を引き継ぐ
function [MRR-1:0] twidFunc;
input [MRR-1:0] cnt;
input [3:0] radix;
reg [MRR-1:0] twid;
begin
// Twiddle Factor
case (radix)
4'h4: twid = {{MRR-2{1'b0}}, cnt[3:2]};
4'h6: twid = {{MRR-2{1'b0}}, cnt[5:4]};
4'h8: twid = {{MRR-2{1'b0}}, cnt[7:6]};
default:
twid = {{MRR-2{1'b0}}, cnt[MRR-1:MRR-2]};
endcase
// Result
twidFunc = cnt ^ twid;
end
endfunction
endmodule // storeData
// *****************************************************************************
回路デザイン > 設計例 [FFT] > コーディング2 次のページ(コーディング3) このページのTOP ▲