昨日の夜、"Hello World"を送信する回路がちゃんと動いて、安心して寝た。今日はPC -> FPGAの受信を書き、とりあえずこちらが送った文字をそのまま返すだけの回路を作った。少しは慣れてきたのか、シミュレーションでデバッグしたあと実機に焼いたら即動いたのでガッツポーズしてしまった。というのも、数日前まではシミュレーションで動くように見えてもビットストリームを作れないような回路とか、実機では動かないような回路を書いてしまっていたので……。
今日、途中でシミュレーションしようとしても「executing analysis and compilation step」のダイアログが出たまま始まらなくなってしまい、何かおかしくなったのかとシミュレータを閉じたり、Vivadoを閉じたり、Windowsを再起動しても直らなかったのでほげ~と言いながらググっていたら以下のような回答が出てきた。
これにある通り、tclコンソールにreset_simulation -simset sim_1 -mode behavioral
と入力すると動くようになった。何だったんだ。
しばらくちゃんと本を読んだり他人のコードを読んだりしていて、今まで(といっても今日で書き始めて1週間なのだが)reg
を意味もなく多用していたことに気づいた。その瞬間の入力の組み合わせだけで問題ない部分はwire
でよい。ていうかなんでreg
ばっかり使ってたんだろうな。
ところで、wire
に対してif
を使おうとしたときfunction
まみれになるのはどうしたらいいんだろう。function
で一つの変数しか返していないので無駄に分割された大量のコードが並ぶようになってしまった。もちろん、function
の返り値をassign {val1, val2} = next_value(...)
のようにしてパック展開(?)で受ければよいのだが、これはこれでまた若干面倒くさい。何かいい書き方があるのを知らないだけな気がするのだけど。SystemVerilogではalways_comb
が使えるのだが。
次にすることを考える。とりあえず文字列を送受信できるようになったので、何かしらの簡単な計算をする回路を作れるはずだ。例えばmd5sumとかはできるだろう。とはいえ一足飛びにそこまで行くのは大変そうなので、まずは単に文字列を覚えてあとで返すようなものを作った方がよさそう。ヴィジュネル暗号とかだと少し楽しいだろうか? もう少し先の話としては、Brainfuckのインタプリタなどは楽しそうだしやってみたいと思っていた。
その前に少しリファクタリングと機能追加をした方がいいかもしれない。例えば、送受信回路にバッファ用のメモリを一枚噛ませることで、処理を中断させずに送信用のデータをためたり、読み込みが終わっているかどうか心配せずに受信データを更新したりできるようにしておきたい。まあそのメモリが溢れたら止めざるを得ないが、ないよりましだろう。特に今は受信データは次のデータが来た瞬間に問答無用で消えてしまうので、かなりタイミングがシビアになっている。これはよくない。
ここまでとりあえずVerilogから勉強していくか、と思ってVerilogの本を読んでいたが、普通にSystemVerilogの文法のさわりだけでも知っているとパク……参考にできるコードの幅が広がるので両方やったほうがよさそう。最終的にChiselまで触る気ではいる(これはRustを勉強する前にC++を勉強する前にCを勉強するようなもの(メリットもあるがコストも高い)なのではないかという気はしたものの、性格的にはその方が自分には合っているのではないかという気がしている。それだけのコストをかけるだけの余裕があるかというのは年々怪しくなっているが……)。
Vivadoで使えるテキストエディタにGVimがあったのでGVimを導入して.vimrcなどを設定したが、vivadoが出してくれるエラーが表示できない。そういうプラグインがあるだろうと思ったが、すぐには見つからなかった。あとで気合を入れて探してみるか。
そうそう、それからコーディングスタイルをとっとと固めておいた方がいいな。どういう風に書くのが普通なのかあまりよく知らないので今はなんか適当に書いてる。
module top(CLK100MHZ, BUTTON, uart_rxd_out, uart_txd_in, led0, led1); input wire CLK100MHZ; input wire BUTTON; input wire uart_txd_in; output wire uart_rxd_out; output wire[2:0] led0; output wire[2:0] led1; wire uart_send_enable; wire[7:0] uart_send_data; wire uart_sending; uart_send send( .clock(CLK100MHZ), .reset(BUTTON), .uart_enable(uart_send_enable), .uart_data(uart_send_data), .uart_busy(uart_sending), .uart_tx(uart_rxd_out), .led(led0) ); assign led1 = 3'b000; wire[7:0] uart_recv_data; wire uart_receiving; wire uart_recv_okay; uart_recv recv( .clock(CLK100MHZ), .reset(BUTTON), .uart_data(uart_recv_data), .uart_busy(uart_receiving), .uart_okay(uart_recv_okay), .uart_rx(uart_txd_in), .led(led1) ); reg[7:0] data; reg data_sent; wire has_data = (~uart_receiving) && uart_recv_okay; assign uart_send_data = data; parameter IDLE_STATE = 0; parameter SEND_STATE = 1; parameter WAIT_STATE = 2; reg [1:0] state = IDLE_STATE; wire[1:0] w_state; assign uart_send_enable = state == SEND_STATE; function [1:0] next_state( input[1:0] state, input uart_sending, input has_data, input data_sent ); if (state == IDLE_STATE) begin if (has_data && (~data_sent)) begin if (uart_sending) begin next_state = WAIT_STATE; end else begin next_state = SEND_STATE; end end else begin next_state = IDLE_STATE; end end else if (state == WAIT_STATE) begin if (uart_sending) begin next_state = WAIT_STATE; end else begin next_state = SEND_STATE; end end else begin // SEND_STATE next_state = IDLE_STATE; end endfunction assign w_state = next_state(state, uart_sending, has_data, data_sent); always @(posedge CLK100MHZ) begin if (BUTTON) begin state <= IDLE_STATE; data <= 8'b0; data_sent <= 1'b0; end else begin state <= w_state; if (uart_recv_okay) begin data <= uart_recv_data; end else begin data <= 8'b0; end if (uart_receiving) begin data_sent <= 1'b0; end else if (state == SEND_STATE) begin data_sent <= 1'b1; end end end endmodule module uart_send(clock, reset, uart_enable, uart_data, uart_busy, uart_tx, led); input wire clock; input wire reset; input wire uart_enable; input wire [7:0] uart_data; output wire uart_busy; output wire uart_tx; output wire [2:0] led; parameter CLK_PER_BIT = 868; // 100 MHz / 115200 Hz parameter IDLE_STATE = 0; parameter SEND_STATE = 1; reg state = IDLE_STATE; wire w_state; reg [9:0] data = 10'b1111111111; wire[9:0] w_data; reg [9:0] clock_counter = 10'b0; wire[9:0] w_clock_counter; reg [3:0] bit_counter = 4'b0; wire[3:0] w_bit_counter; assign uart_tx = data[0]; assign uart_busy = (state == IDLE_STATE) ? 1'b0 : 1'b1; assign led[2:0] = (state == IDLE_STATE) ? 3'b001 : 3'b010; // ---------------------------------------------------------------------- // update state function [0:0] next_state( input state, input enable, input[9:0] clock_counter, input[3:0] bit_counter ); if (state == IDLE_STATE) begin if(enable) begin next_state = SEND_STATE; end else begin next_state = IDLE_STATE; end end else begin // SEND_STATE if (clock_counter == CLK_PER_BIT-1) begin if (bit_counter == 4'd9) begin next_state = IDLE_STATE; end else begin next_state = SEND_STATE; end end else begin next_state = SEND_STATE; end end endfunction assign w_state = next_state(state, uart_enable, clock_counter, bit_counter); // ---------------------------------------------------------------------- // update clock counter function [9:0] next_clock_counter( input state, input[9:0] clock_counter ); if (state == IDLE_STATE) begin next_clock_counter = 10'b0; end else begin if (clock_counter == CLK_PER_BIT-1) begin next_clock_counter = 10'b0; end else begin next_clock_counter = clock_counter + 1; end end endfunction assign w_clock_counter = next_clock_counter(state, clock_counter); // ---------------------------------------------------------------------- // update number of bits sent function [3:0] next_bit_counter( input state, input[9:0] clock_counter, input[3:0] bit_counter ); if(state == IDLE_STATE) begin next_bit_counter = 4'b0; end else begin if (clock_counter == CLK_PER_BIT-1) begin if (bit_counter == 4'd9) begin next_bit_counter = 4'd0; end else begin next_bit_counter = bit_counter + 1; end end else begin next_bit_counter = bit_counter; end end endfunction assign w_bit_counter = next_bit_counter(state, clock_counter, bit_counter); // ---------------------------------------------------------------------- // update data to send function [9:0] next_data( input state, input enable, input[7:0] uart_data, input[9:0] data, input[9:0] clock_counter ); if (state == IDLE_STATE) begin if (enable) begin next_data = {1'b1, uart_data, 1'b0}; end else begin next_data = data; end end else begin if (clock_counter == CLK_PER_BIT - 1) begin next_data = {1'b1, data[9:1]}; end else begin next_data = data; end end endfunction assign w_data = next_data(state, uart_enable, uart_data, data, clock_counter); // ---------------------------------------------------------------------- // update registers always @(posedge clock) begin if (reset) begin state <= IDLE_STATE; clock_counter <= 10'b0; bit_counter <= 4'b0; data <= 10'b1111111111; end else begin state <= w_state; clock_counter <= w_clock_counter; bit_counter <= w_bit_counter; data <= w_data; end end endmodule module uart_recv(clock, reset, uart_data, uart_busy, uart_okay, uart_rx, led); input wire clock; input wire reset; output wire [7:0] uart_data; output wire uart_busy; output wire uart_okay; input wire uart_rx; output wire [2:0] led; parameter CLK_PER_BIT = 868; // 100 MHz / 115200 Hz parameter IDLE_STATE = 0; parameter CHCK_STATE = 1; // checking start bit parameter RECV_STATE = 2; parameter STOP_STATE = 3; // receiving stop bit reg [1:0] state = IDLE_STATE; wire[1:0] w_state; reg [9:0] clock_counter = 10'b0; wire[9:0] w_clock_counter; reg [3:0] bit_counter = 4'b0; wire[3:0] w_bit_counter; reg [9:0] data = 10'b1111111111; wire[9:0] w_data; assign uart_data = data[8:1]; // except start/stop bits assign uart_okay = (data[0:0] == 1'b0) && (data[9:9] == 1'b1); // start with 0, end with 1 assign uart_busy = (state == RECV_STATE) ? 1'b1 : 1'b0; assign led[2:0] = (state == IDLE_STATE) ? 3'b001 : (state == CHCK_STATE) ? 3'b010 : (state == RECV_STATE) ? 3'b100 : 3'b111; // ---------------------------------------------------------------------- // update state wire rx_is_zero; assign rx_is_zero = (uart_rx == 1'b0); function [1:0] next_state( input[1:0] state, input uart_rx, input rx_is_zero, input[9:0] clock_counter, input[3:0] bit_counter ); if (state == IDLE_STATE) begin if(rx_is_zero) begin next_state = CHCK_STATE; end else begin next_state = IDLE_STATE; end end else if (state == CHCK_STATE) begin if (clock_counter == (CLK_PER_BIT / 2) - 1) begin if (rx_is_zero) begin next_state = RECV_STATE; end else begin next_state = IDLE_STATE; end end else begin next_state = CHCK_STATE; end end else if (state == RECV_STATE) begin if (clock_counter == CLK_PER_BIT-1) begin if (bit_counter == 4'd8) begin next_state = STOP_STATE; end else begin next_state = RECV_STATE; end end else begin next_state = RECV_STATE; end end else begin // STOP_STATE if (clock_counter == (CLK_PER_BIT / 2) - 1) begin next_state = IDLE_STATE; end else begin next_state = STOP_STATE; end end endfunction assign w_state = next_state(state, uart_rx, rx_is_zero, clock_counter, bit_counter); // ---------------------------------------------------------------------- // update clock counter function [9:0] next_clock_counter( input[1:0] state, input[9:0] clock_counter ); if (state == IDLE_STATE) begin next_clock_counter = 10'b0; end else if(state == CHCK_STATE) begin if (clock_counter == CLK_PER_BIT / 2 - 1) begin next_clock_counter = 10'b0; end else begin next_clock_counter = clock_counter + 1; end end else begin if (clock_counter == CLK_PER_BIT-1) begin next_clock_counter = 10'b0; end else begin next_clock_counter = clock_counter + 1; end end endfunction assign w_clock_counter = next_clock_counter(state, clock_counter); // ---------------------------------------------------------------------- // update number of bits sent function [3:0] next_bit_counter( input[1:0] state, input[9:0] clock_counter, input[3:0] bit_counter ); if(state == IDLE_STATE) begin next_bit_counter = 4'b0; end else if(state == CHCK_STATE) begin next_bit_counter = 4'b0; end else begin if (clock_counter == CLK_PER_BIT-1) begin if (bit_counter == 4'd9) begin next_bit_counter = 4'd0; end else begin next_bit_counter = bit_counter + 1; end end else begin next_bit_counter = bit_counter; end end endfunction assign w_bit_counter = next_bit_counter(state, clock_counter, bit_counter); // ---------------------------------------------------------------------- // update received data function [9:0] next_data( input[1:0] state, input uart_rx, input[9:0] data, input[9:0] clock_counter ); if (state == IDLE_STATE) begin next_data = data; end else if (state == CHCK_STATE) begin if (uart_rx == 1'b0) begin next_data = 10'b0111111111; end else begin next_data = data; end end else if (state == RECV_STATE) begin if (clock_counter == CLK_PER_BIT - 1) begin next_data = {uart_rx, data[9:1]}; end else begin next_data = data; end end else begin next_data = data; end endfunction assign w_data = next_data(state, uart_rx, data, clock_counter); // ---------------------------------------------------------------------- // update registers always @(posedge clock) begin if (reset) begin state <= IDLE_STATE; clock_counter <= 10'b0; bit_counter <= 4'b0; data <= 10'b1111111111; end else begin state <= w_state; clock_counter <= w_clock_counter; bit_counter <= w_bit_counter; data <= w_data; end end endmodule