またしてもこの本でVerilogだ。
今回は7-1節の
PS/2インターフェース。
手近に
PS/2マウスがなかったため、キーボードの入力を読み込む
PS/2リーダーとして実装した。
まずは
PS/2リーダーモジュールの
Verilog、本のコードからはいくらか変更してある。
- ステートマシンは読み込み専用に簡略化してある。
- レジスタマップを変更。
- アドレス0: PS/2リーダーモジュールの状態レジスタ(PS2STATUS、8bit)、[0]PS/2からの読み出しデータフラグ、1でready、0を書き込むとクリアできる、残りのビットはnull
- アドレス1: PS/2から読み出したデータ(PS2RDARA、8bit)
- さらにステートの定義も変更。本ではパリティを受信するステートをSETFLGとしているが、ここではそのままGETBITとしている(後でパリティチェックを実装予定)。STOPBIT(ストップビット待ちの状態)で状態レジスタを変更し、同時にPS/2読み出し値をリードバスへセットしている。
- PS/2クロックの立下り検出を2段のFFにしている。いろいろやってみたが1段(FF一個とPS/2クロックで直接立下り検出をする)では動作が安定しなかった。
module PS2Reader(
// Avalon Bus
input wire in_clk,
input wire in_reset,
input wire in_address,
input wire in_write,
input wire in_read,
input wire [7:0] in_write_data,
output wire [7:0] out_read_data,
// Ports
inout wire io_PS2CLK,
inout wire io_PS2DATA);
//
// PS2 Reader State Machine
//
reg [3:0] ps2_state;
// state definition
parameter HALT = 4'h0;
parameter GETBIT = 4'h1;
parameter STOPBIT = 4'h2;
//
always @(posedge in_clk, posedge in_reset)
begin
if (in_reset)
ps2_state <= HALT;
else
begin
case (ps2_state)
HALT:
begin
// wait for START BIT
if (f_ps2clk_fall && (io_PS2DATA == 1'b0))
ps2_state <= GETBIT;
else
ps2_state <= HALT;
end
GETBIT:
begin
if (f_ps2clk_fall && (counter_bit_position) == 4'h8)
ps2_state <= STOPBIT;
else
ps2_state <= GETBIT;
end
STOPBIT:
begin
// wait for STOP BIT
if (f_ps2clk_fall && (io_PS2DATA == 1'b1))
ps2_state <= HALT;
else
ps2_state <= STOPBIT;
end
default:
ps2_state <= HALT;
endcase
end
end
//
// PS2 CLK falling edge detection = f_ps2clkfall
//
reg [1:0] ff_ps2clk_fall_detection;
wire f_ps2clk_fall;
//
always @(posedge in_clk, posedge in_reset)
begin
if (in_reset)
ff_ps2clk_fall_detection <= 2'b00;
else
ff_ps2clk_fall_detection <= {ff_ps2clk_fall_detection[0], io_PS2CLK};
end
//
assign f_ps2clk_fall = (ff_ps2clk_fall_detection == 2'b10)? 1'b1: 1'b0;
//
// S-P bit position counter
//
reg [3:0] counter_bit_position;
//
always @(posedge in_clk, posedge in_reset)
begin
if (in_reset)
counter_bit_position <= 4'h0;
else if (ps2_state == HALT)
counter_bit_position <= 4'h0;
else if ((ps2_state == GETBIT) && f_ps2clk_fall)
counter_bit_position <= counter_bit_position + 4'h1;
end
//
// PS2DATA read (S-P read)
//
reg [9:0] _10bit_SP_buffer;
//
always @(posedge in_clk, posedge in_reset)
begin
if (in_reset)
_10bit_SP_buffer <= 10'b00_0000_0000;
else if ((ps2_state == GETBIT) && f_ps2clk_fall)
_10bit_SP_buffer <= {io_PS2DATA, _10bit_SP_buffer[9:1]};
end
//
// flag when read data is ready
//
reg f_PS2_data_ready;
//
always @(posedge in_clk, posedge in_reset)
begin
if (in_reset)
f_PS2_data_ready <= 1'b0;
else if ((in_write == 1'b1) && (in_address == 1'b0))
f_PS2_data_ready <= in_write_data[0];
else if ((ps2_state == STOPBIT) && f_ps2clk_fall)
f_PS2_data_ready <= 1'b1;
end
//
// set read data
//
assign out_read_data = (in_read == 1'b0)? 8'h0:
(in_address == 1'b0)? {7'h0, f_PS2_data_ready}: _10bit_SP_buffer[8:1];
//
endmodule
このモジュールをQsysでNios IIに組み込む。
方法はこちらと同様。
今回は
- Nios II/e
- On-chip Memory (RAM or ROM) 8192bytes
- System ID Peripheral
- JTAG UART
- (自作の)ps2reader
そして、キーボードの値を7セグLEDに表示するために、
を組み込んだ。
QsysでNios IIと周辺機器IPを組み込んだ統合モジュールを作る方法は、こちらと同様。
トップモジュールはこんな感じだ。
module nios2ps2reader(
input wire in_clk,
input wire [9:0] in_switch,
input wire [2:0] in_button,
output wire [9:0] out_led,
output wire [7:0] seven_segment_0,
output wire [7:0] seven_segment_1,
output wire [7:0] seven_segment_2,
output wire [7:0] seven_segment_3,
inout PS2CLK,
inout PS2DATA);
//
assign reset_n = in_button[0];
//
// modules
//
nios2ps2reader_qsys nios2ps2reader_qsys(
.clk_clk(in_clk), // clk.clk
.reset_reset_n(reset_n), // reset.reset_n
.ps2reader_0_conduit_end_io_ps2clk_export(PS2CLK), // ps2reader_0_conduit_end_io_ps2clk.export
.ps2reader_0_conduit_end_io_ps2data_export(PS2DATA), // ps2reader_0_conduit_end_io_ps2data.export
.pio_0_export({seven_segment_1, seven_segment_0}) // pio_0.export
);
//
// terminate unused LEDs
//
assign out_led[0] = ~PS2CLK;
assign out_led[1] = ~PS2DATA;
assign out_led[9:2] = 8'b0000_0000;
assign seven_segment_2 = 8'b1111_1111;
assign seven_segment_3 = 8'b1111_1111;
//
endmodule
PS/2クロック(PS2CLK)はピンP22に、PS/2データ(PS2DATA)はピンP21に接続する。
あとはいつもと同じだ。
最後に、Nios IIのソースコード。
Nios IIでps2readerの状態レジスタをpollingして、PS/2からの読み出しデータが準備できたらその値を読み込み、7セグLED用のデータに変換して、PIOに出力する。
#include "system.h"
#include "io.h"
int main()
{
int PS2State, PS2Data;
unsigned short seven_seg_out;
unsigned char out_MSB, out_LSB;
IOWR_16DIRECT(PIO_0_BASE, 0, 0xffff);
/* Event loop never exits. */
while (1)
{
PS2State = IORD_8DIRECT(PS2READER_0_BASE, 0);
if (PS2State == 1)
{
PS2Data = IORD_8DIRECT(PS2READER_0_BASE, 1);
IOWR_8DIRECT(PS2READER_0_BASE, 0, 0x00);
if (PS2Data != 0xf0)
{
alt_printf("%x ", PS2Data);
switch(PS2Data / 16)
{
case 0x0:
out_MSB = 0xc0;
break;
case 0x1:
out_MSB = 0xf9;
break;
case 0x2:
out_MSB = 0xa4;
break;
case 0x3:
out_MSB = 0xb0;
break;
case 0x4:
out_MSB = 0x99;
break;
case 0x5:
out_MSB = 0x92;
break;
case 0x6:
out_MSB = 0x82;
break;
case 0x7:
out_MSB = 0xd8;
break;
case 0x8:
out_MSB = 0x80;
break;
case 0x9:
out_MSB = 0x90;
break;
case 0xa:
out_MSB = 0x88;
break;
case 0xb:
out_MSB = 0x83;
break;
case 0xc:
out_MSB = 0xc6;
break;
case 0xd:
out_MSB = 0xa1;
break;
case 0xe:
out_MSB = 0x86;
break;
case 0xf:
out_MSB = 0x8e;
break;
default:
out_MSB = 0xff;
break;
}
switch(PS2Data % 16)
{
case 0x0:
out_LSB = 0xc0;
break;
case 0x1:
out_LSB = 0xf9;
break;
case 0x2:
out_LSB = 0xa4;
break;
case 0x3:
out_LSB = 0xb0;
break;
case 0x4:
out_LSB = 0x99;
break;
case 0x5:
out_LSB = 0x92;
break;
case 0x6:
out_LSB = 0x82;
break;
case 0x7:
out_LSB = 0xd8;
break;
case 0x8:
out_LSB = 0x80;
break;
case 0x9:
out_LSB = 0x90;
break;
case 0xa:
out_LSB = 0x88;
break;
case 0xb:
out_LSB = 0x83;
break;
case 0xc:
out_LSB = 0xc6;
break;
case 0xd:
out_LSB = 0xa1;
break;
case 0xe:
out_LSB = 0x86;
break;
case 0xf:
out_LSB = 0x8e;
break;
default:
out_LSB = 0xff;
break;
}
seven_seg_out = ((unsigned short) out_MSB) * 256 + ((unsigned short) out_LSB);
IOWR_16DIRECT(PIO_0_BASE, 0, seven_seg_out);
}
}
}
return 0;
}
今回大きくハマったのは、PIOへの出力。
Niosコンソールにはキーボードのコードがちゃんと出力されるのに、7セグLEDには出ない。
- (int)を16bitだと思い込んでいた、16bitは(short)
- signedであることを忘れていた、seven_seg_out、out_MSB、out_LSBともunsignedにする必要がある
などなど。
型定義を厳密に書かなくてはいけないのは、組み込みの基本だ。
まずいまずい。