| | | | |
I/O Name | I/O Direction | Loc | Bank | I/O Std. |
| | | | |
| | | | |
SW | Input | M13 | BANK3 | |
| | | | |
LED | Output | K12 | BANK3 | |
| | | | |
- User Constraintsの、Assign Package Pinsで、書き換えられるんだけど、なんかうまく行かない。
- Edit Constraints (Text)から、開いてみると2重に定義されてしまってる。
- こちらを直すと、ちゃんと動作した。
1JTAGを使ってコンフィグレーションPROMに書き込む場合でも、CCLKを選択する。
2何でこんな面倒な仕様なんだろう。。自動的に変換して書けるようにもすれば良いのに。。。
3一度使い方が分かったはずだったんだけど、久しぶりに弄ったら嵌ってしまった。なんて分かり難いツールなんだ。。。
[top]
- ここまで来て、いきなり回路を考えるのは厳しそうって思えてきた。
- まずは単純な回路で一通り試してからの方が良さそうだ。
- 簡単な3ビットUpカウンタを書いてみる。→シュミレーションやりやすいように短く
- 非同期リセット、プリセット付き
`timescale 1ns / 1ps
module counter3(d, q, ld, rst, clk);
input [2:0] d;
output [2:0] q;
input ld;
input rst;
input clk;
reg [2:0] q; // alwaysから変化させるので、regとしても指定
always @(posedge clk or posedge rst) begin
// 非同期リセットなので、センシビリティリストにrstを入れる
if (rst == 1'b1) begin
q <= 3'b000; // 非同期リセット
end else if (ld == 1'b1) begin
q <= d; // 同期プリセット
end else begin
q <= q + 1'b1; // Upカウント
end
end
endmodule
- 書いてみると、VHDLとそんなに違いはないみたい。
- Verilogではalwaysが、VHDLのprocess文に近いようだ。
- alwaysやinitialから変化させる場合は、regにする必要があるらしい。
- regが記憶素子、wireが接続素子ということだが、いまいち使い分けが分かり難いんだけど。
- xc3s200-4ft256を右クリックして、NewSourceで Verilog Test Fixture を選択する。
- すると、テストベンチのスケルトンのようなものが出来る。
- ディレイを使って、入力信号を記述する。
`timescale 1ns / 1ps
module test_v;
// Inputs
reg [2:0] d;
reg ld;
reg rst;
reg clk;
// Outputs
wire [2:0] q;
// Instantiate the Unit Under Test (UUT)
counter3 uut (
.d(d),
.q(q),
.ld(ld),
.rst(rst),
.clk(clk)
);
parameter CYCLE = 100; // 1サイクルを100と定義する
always #(CYCLE/2) clk = ~clk; // クロック生成
initial begin
#0; // 最初は全部クリア
d = 3'b000;
ld = 0;
rst = 0;
clk = 0;
// Wait 100 ns for global reset to finish
#(CYCLE);
rst = 1; // リセット
#(CYCLE);
rst = 0;
#(CYCLE*10);
d = 3'b011; // プリセット値
ld = 1; // データロード
#(CYCLE);
d = 3'b000;
ld = 0;
#(CYCLE*10);
end
endmodule
という感じで、カウンタのテストベンチを書いてみた。
- ModelSim Simulatorの、Simulate Behavioral ModelをダブルクリックするとModelSimが起動する。
- シュミレーション時間を、1usにして4回RUNしてみる。
- ちゃんと動いてるみたいだな〜。まぁこれなら、Verilogも使えそうだ。
- 波形のRADIXの設定等は Fileメニューから Save(例えば wave.do)して、必要な時にコマンドラインから do wave.doとすれば良い。
[top]
- UARTの受信だけ考えてみる。とりあえずの仕様としては、
- データ長は8ビットで、LSBファースト
- パリティなし、1ストップビット
- ボーレートは、9600bps固定
- スターターキット基板上の50MHzを分周して、9600bpsのボーレート用クロックを作る。
- 受信用に9600bpsの4倍のクロックを作るとすると、50000000/9600/4 = 約1302分周か。
- リプルカウンタで分周器を作ってサンプリング回路のクロックにしたいところだけど、同期回路ではクロックが複数になってしまうのは気持ちが悪い。
- 分周器は同期回路では複雑になってしまって嫌なんだけど、とりあえず制御信号として作ってみよう。
`timescale 1ns / 1ps
module clkgen(rxck, txck, rst, clk);
output rxck;
output txck;
input rst;
input clk;
reg rxck; // alwaysから変化させるので、regとしても指定
reg txck;
reg [10:0] rxcount;
reg [1:0] txcount;
always @(posedge clk or posedge rst) begin
// 非同期リセットなので、センシビリティリストにrstを入れる
if (rst == 1'b1) begin
rxcount <= 0; // 非同期リセット
rxck <= 0;
end else if (count == 1301) begin // 1302分周
rxcount <= 0;
rxck <= 1;
end else begin
rxcount <= count + 1; // Upカウント
rxck <= 0;
end
end
always @(posedge clk or posedge rst) begin
// 非同期リセットなので、センシビリティリストにrstを入れる
if (rst == 1'b1) begin
txcount <= 0; // 非同期リセット
txck <= 0;
end else if (count == 3) begin
txck <= 1; // 4分周
end else if (rxck == 1'b1) begin
txcount <= txcount + 1; // Upカウント
txck <= 0;
end
end
endmodule
- デューティとかは敢えて考えず、制御用に1クロック幅の信号を作る。
- txckはボーレート、rxckはボーレートの4倍のタイミングのクロック制御信号。
- 分周器で作ったボーレートの4倍のタイミング(rxck)で、RXDからのデータをシフト。
- 外からの信号ということで、シフトレジスタで受けて、ついでに簡単なノイズ除去を行う。
`timescale 1ns / 1ps
module ncan(rxd, buf, rst, rxck, clk);
input rxd;
output [7:0] buf;
input rst;
input rxck;
input clk;
reg [7:0] buf;
wire cont;
always @(posedge clk or posedge rst) begin
if (rst == 1'b1) begin
buf <= 8'b00000000; // 非同期リセット
end else if (rxck == 1'b1) begin
buf <= buf >> 1; // シフト
buf[7] <= rxd;
buf[4] <= ncout;
end
end
function nc;
input [2:0] in;
if (in == 3'b000)
nc = 0; // 3回連続0で0出力
else if (in == 3'b111)
nc = 1; // 3回連続1で1出力
endfunction
assign ncout = nc(buf[7:5]); // これが簡易雑音除去回路ってことで
endmodule
- これは無駄にシフトレジスタで受けてるんだけど、自分としてはデータが流れてる感じが好きなので。
- 実際は、ノイズ除去もなしで単純にFFで受けるぐらいでも良いと思う。
- シリアルデータは、最初にスタートビット、データ8ビット、最後にストップビットが来るので、10状態のカウンタを作れば良い。
- スタートビットを検出したら、ステート用のカウンタをリセット。
`timescale 1ns / 1ps
module state(buf, rst, state, rxck, rxck4, clk);
input [7:0] buf;
input rst;
output[3:0] state;
input rxck;
output rxck4;
input clk;
reg [3:0] state;
reg [1:0] rxcount4;
assign start = (buf[3:0] == 4'b0011)?1:0; // スタートビット検出
always @(posedge clk or posedge rst) begin
// 非同期リセットなので、センシビリティリストにrstを入れる
if (rst == 1'b1) begin
rxcount4 <= 0; // 非同期リセット
end else if (rxck == 1 && start == 1) begin
rxcount4 <= 0; // startで同期リセット
end else if (rxck == 1) begin
rxcount4 <= txcount4 + 1; // Upカウント
end
end
assign rxck4 = (rcount4 == 2'b11)?1:0; // rxck4のタイミングは調整必要かも
always @(posedge clk or posedge rst) begin
// 非同期リセットなので、センシビリティリストにrstを入れる
if (rst == 1'b1) begin
state <= 0; // 非同期リセット
start <= 0;
end else if (rxck4 == 1'b1 && (start == 1'b1 || state == 4'b1001)) begin
state <= 0; // スタートビット検出、状態9で同期リセット
start <= 0;
end else if (rxck4 == 1'b1) begin
state <= state + 1; // Upカウント
end
end
endmodule
- ボーレートのタイミングは、rxck4じゃなくてtxckでも良いかもしれないが、
- このステートマシンのカウンタは、バイナリカウンタ構成だが、これだと同期カウンタになっちゃうので、出来上がりは結構複雑になる。
- これはテキトウに作ってるので、こんな風にしたけど、実際はあまりお勧め出来ない。
- 10状態であれば、5ビットのジョンソンカウンタでも良いんじゃないかと思う。
- その方が単純なカウンタになるし、デコードが簡単だし変なハザードも出ない。
- ステートマシンが出来れば、あとは単純にパラレル変換するだけだ。
- ボーレートのタイミング(rxck4)でデータをシフトする。
`timescale 1ns / 1ps
module seripara(out, rst, state, rxck4, clk);
output out;
input rst;
input[3:0] state;
input rxck4;
input clk;
reg [7:0] rxbuf;
reg [7:0] out;
always @(posedge clk or posedge rst) begin
// 非同期リセットなので、センシビリティリストにrstを入れる
if (rst == 1'b1) begin
rxbuf <= 0; // 非同期リセット
out <= 0;
end else if (state == 0) begin
; // 状態0はなにもしない
end else if (rxck4 == 1'b1 && state == 9) begin
out <= rxbuf; // 状態9で出力
end else if (rxck4 == 1'b1) begin
rxbuf <= rxbuf>>1; // それ以外の状態でシフト
rxbuf[7] <= buf[4]; // シリバラ変換(取り込み位置も調整要か?)
end
end
endmodule
- シリパラ変換のデータ取り込みは、スタートビット検出位置からズラしてるつもり。。
[top]
いろいろ参考にさせて頂いたサイト(感謝)
- 上で検討したのと全然別なんだけど、 こちら にUARTサンプルコードがあったので、使わせてもらった。
- クロックと同期していた方が良かったので、ちょっとそれだけ変えてある。
- 仕様が不明なため、シュミレーションで動作確認した。
- txclkは送信ボーレートと同じ、rxclkは受信ボーレートx16
- 送信は、tx_dataに送信データセット⇒ld_tx_data=0→1→0⇒tx_empty=1で送信完了
- 受信は、rx_empty=0で受信完了⇒uld_rx_data=0→1→0⇒rx_dataに受信データがセットされる
- 受信完了後、unloadしないとデータが読めないので注意
- tx_enable,rx_enableは常にEnableでも良い様だ
- このコードは、clkに同期するように、オリジナルを書き換えてある
- rx_clkはtx_clk*16である必要がある。rx_clk,tx_clkは、1クロック幅のenable信号
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: K.I
// Module Name: uart
// Description: UARTモジュール
// http://www.asic-world.com/examples/verilog/uart.htmlのUARTサンプルコード
// 仕様が不明なため、シュミレーションで動作確認した。
// txclkは送信ボーレートと同じ、rxclkは受信ボーレートx16
// 送信は、tx_dataに送信データセット⇒ld_tx_data=0→1→0⇒tx_empty=1で送信完了
// 受信は、rx_empty=0で受信完了⇒uld_rx_data=0→1→0⇒rx_dataに受信データがセットされる
// 受信完了後、unloadしないとデータが読めないので注意
// tx_enable,rx_enableは常にEnableでも良い様だ
// このコードは、clkに同期するように、オリジナルを書き換えてある
// rx_clkはtx_clk*16である必要がある。rx_clk,tx_clkは、1クロック幅のenable信号
//////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------
// Design Name : uart
// File Name : uart.v
// Function : Simple UART
// Coder : Deepak Kumar Tala
// Modify : Kohji Itoh
//-----------------------------------------------------
module uart (
reset ,
clk ,
txclk ,
ld_tx_data ,
tx_data ,
tx_enable ,
tx_out ,
tx_empty ,
rxclk ,
uld_rx_data ,
rx_data ,
rx_enable ,
rx_in ,
rx_empty
);
// Port declarations
input reset ;
input clk ;
input txclk ;
input ld_tx_data ;
input [7:0] tx_data ;
input tx_enable ;
output tx_out ;
output tx_empty ;
input rxclk ;
input uld_rx_data ;
output [7:0] rx_data ;
input rx_enable ;
input rx_in ;
output rx_empty ;
// Internal Variables
reg [7:0] tx_reg ;
reg tx_empty ;
reg tx_over_run ;
reg [3:0] tx_cnt ;
reg tx_out ;
reg [7:0] rx_reg ;
reg [7:0] rx_data ;
reg [3:0] rx_sample_cnt ;
reg [3:0] rx_cnt ;
reg rx_frame_err ;
reg rx_over_run ;
reg rx_empty ;
reg rx_d1 ;
reg rx_d2 ;
reg rx_busy ;
// UART RX Logic
always @ (posedge clk or posedge reset) begin
if (reset) begin
rx_reg <= 0;
rx_data <= 0;
rx_sample_cnt <= 0;
rx_cnt <= 0;
rx_frame_err <= 0;
rx_over_run <= 0;
rx_empty <= 1;
rx_d1 <= 1;
rx_d2 <= 1;
rx_busy <= 0;
end
else if (uld_rx_data) begin
// Uload the rx data
rx_data <= rx_reg;
rx_empty <= 1;
end
else if (rxclk) begin
// Synchronize the asynch signal
rx_d1 <= rx_in;
rx_d2 <= rx_d1;
// Receive data only when rx is enabled
if (rx_enable) begin
// Check if just received start of frame
if (!rx_busy && !rx_d2) begin
rx_busy <= 1;
rx_sample_cnt <= 1;
rx_cnt <= 0;
end
// Start of frame detected, Proceed with rest of data
if (rx_busy) begin
rx_sample_cnt <= rx_sample_cnt + 1;
// Logic to sample at middle of data
if (rx_sample_cnt == 7) begin
if ((rx_d2 == 1) && (rx_cnt == 0)) begin
rx_busy <= 0;
end else begin
rx_cnt <= rx_cnt + 1;
// Start storing the rx data
if (rx_cnt > 0 && rx_cnt < 9) begin
rx_reg[rx_cnt - 1] <= rx_d2;
end
if (rx_cnt == 9) begin
rx_busy <= 0;
// Check if End of frame received correctly
if (rx_d2 == 0) begin
rx_frame_err <= 1;
end else begin
rx_empty <= 0;
rx_frame_err <= 0;
// Check if last rx data was not unloaded,
rx_over_run <= (rx_empty) ? 0 : 1;
end
end
end
end
end
end
else begin // !rx_enable
rx_busy <= 0;
end
end
end
// UART TX Logic
always @ (posedge clk or posedge reset) begin
if (reset) begin
tx_reg <= 0;
tx_empty <= 1;
tx_over_run <= 0;
tx_out <= 1;
tx_cnt <= 0;
end
else if (txclk) begin
if (tx_enable && !tx_empty) begin
tx_cnt <= tx_cnt + 1;
if (tx_cnt == 0) begin
tx_out <= 0;
end
if (tx_cnt > 0 && tx_cnt < 9) begin
tx_out <= tx_reg[tx_cnt -1];
end
if (tx_cnt == 9) begin
tx_out <= 1;
tx_cnt <= 0;
tx_empty <= 1;
end
end
if (!tx_enable) begin
tx_cnt <= 0;
end
end
else if (ld_tx_data) begin
if (!tx_empty) begin
tx_over_run <= 0;
end
else begin
tx_reg <= tx_data;
tx_empty <= 0;
end
end
end
endmodule
[top]
- クロックに同期していない信号をそのまま同期カウンタに入れたりすると、
- 各ビットに入力されるタイミングはバラバラなので、滅茶苦茶な結果になる。
- これは基本的にはFFで受けて、クロックで同期してから入力すれば良いが、
- 非同期の入力は不安定なので、FFの出力も不安定4になってしまう場合がある
- このような不安定状態を、メタステーブルと言うらしい。
- メタステーブルは再現性が無く、検証も難しいというか無理。
- メタステーブルの対策としては、例えば2段のFFで受けて、クロックで同期してやる。
- FF間の配線は短くなるように制約をつける方が良いらしい5
- それで同期しても、多ビット入力の場合は、当然タイミングのズレはあるので、
- ズレ量よりも後に、データを取り込むようにしないと問題になるので要注意
- 自分は、同期リセットはリセットが掛からないことが心配なので、主に非同期リセットを使うんだけど、
- こちらで紹介されているように、解除は同期式でやった方が良いらしい
4不安定状態が長時間続いたり、発振状態になるかもしれない。
5不安定状態の影響を最小に、短時間で収めたいということだと思う。
[top]
- alwaysの構文が複雑で、FFかLatchか分からないと言っているらしい。
- 自分の場合は、if - else if - else if という構文を書いているつもりで、
- うっかり、if - if - else if と記述してしまって、暫く気がつかなかった。。。
- エラーメッセージが具体的じゃなくて分かりずらい。ピン番号とか出してくれれば良いんだけど。。
[top]
[電子工作関連に戻る]