周辺機器を追加(LEDコントローラ)
FPGA上でCPUが動くようになったので、LEDやUARTなどの周辺機器を追加していきたい。
手始めに Odeeen から LED を点灯できるようにする。
実装
LEDコントローラを雑に作成し、アドレス 0xf0001000 へ書き込んだ値を LED へ出力できるようにする。
データバスには「メモリコントローラ」と「LEDコントローラ」のふたつがぶら下がってるので、指定したアドレスによって片方のコントローラのみが有効になるような制御も追加する。
code:verilog
// メモリマップ
assign bram_en = (mem_addr < 8192) ? 1'b1 : 1'b0;
assign uart_data_en = (mem_addr == 32'hf0000000) ? 1'b1 : 1'b0;
assign uart_ctl_en = (mem_addr == 32'hf0000004) ? 1'b1 : 1'b0;
assign led_ctl_en = (mem_addr == 32'hf0001000) ? 1'b1 : 1'b0;
CPUから周辺機器への信号線はCPUから出ている信号をそのまま周辺機器のコントローラへ繋いでいいが、周辺機器からCPUへ向かう信号(readyとrdata)は、選択してる周辺機器の信号のみがCPUへ取り込まれるようマルチプレクサで制御する必要がある。
code:verilog
// 周辺機器 => CPU
assign mem_ready = (bram_en) ? bram_mem_ready :
(led_ctl_en) ? led_ctl_mem_ready : 1'b0;
assign mem_rdata = (bram_en) ? bram_mem_rdata :
(led_ctl_en) ? led_ctl_mem_rdata : 32'h0;
諸々修正したものがこちら。
code:ulx3s_top.sv
//------------------------------------------------------------------
// ULX3S 上で Odeeen CPU を動かすためのトップモジュール
//------------------------------------------------------------------
module ulx3s_top(
input wire clk_25mhz,
);
// CPU のインスタンス
cpu cpu_inst(
.clk(clk),
.reset_n(reset_n),
.mem_valid(mem_valid),
.mem_instr(mem_instr),
.mem_ready(mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(mem_rdata),
.peek(peek)
);
logic clk, reset_n;
logic mem_valid, mem_ready, mem_instr;
logic 31:0 mem_addr, mem_wdata, mem_rdata; // デバッグ用の信号線
// メモリコントローラのインスタンス
bram_controller bram_ctl (
.clk(clk),
.reset_n(reset_n),
.mem_valid(bram_en && mem_valid),
.mem_ready(bram_mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(bram_mem_rdata)
);
logic bram_mem_ready;
logic 31:0 bram_mem_rdata; // LED コントローラのインスタンス
led_controller led_ctl (
.clk(clk),
.reset_n(reset_n),
.mem_valid(led_ctl_en && mem_valid),
.mem_ready(led_ctl_mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(led_ctl_mem_rdata)
);
logic led_ctl_mem_ready;
logic 31:0 led_ctl_mem_rdata; //------------------------------------------------------------------
// 周辺機器との接続
//------------------------------------------------------------------
// メモリマップ
assign bram_en = (mem_addr < 8192) ? 1'b1 : 1'b0;
assign uart_data_en = (mem_addr == 32'hf0000000) ? 1'b1 : 1'b0;
assign uart_ctl_en = (mem_addr == 32'hf0000004) ? 1'b1 : 1'b0;
assign led_ctl_en = (mem_addr == 32'hf0001000) ? 1'b1 : 1'b0;
// 周辺機器 => CPU
assign mem_ready = (bram_en) ? bram_mem_ready :
(led_ctl_en) ? led_ctl_mem_ready : 1'b0;
assign mem_rdata = (bram_en) ? bram_mem_rdata :
(led_ctl_en) ? led_ctl_mem_rdata : 32'h0;
//------------------------------------------------------------------
// 外部信号線との接続
//------------------------------------------------------------------
assign clk = clk_25mhz;
assign reset_n = btn0; // btn0 は押すとデアサートされる assign led = led_ctl_mem_rdata7:0; endmodule
// LED コントローラを雑に実装
module led_controller (
input wire clk,
input wire reset_n,
input wire mem_valid,
output logic mem_ready,
input wire 31:0 mem_addr, // 使わない input wire 31:0 mem_wdata, input wire 3:0 mem_wstrb, // 0'b1111 の場合は書き込み output logic 31:0 mem_rdata, );
always_ff @(posedge clk) begin
if (!reset_n) begin
led_data_reg <= 8'b0;
end else begin
if (mem_valid && (mem_wstrb == 4'b1111)) begin
led_data_reg <= mem_wdata7:0; end
end
end
assign mem_rdata = { 25'b0, led_data_reg };
assign mem_ready = 1'b1;
endmodule
LEDコントローラを追加したら、bram_controller のプログラムを以下のように修正し、LED へ 1001_1001 を出力してみる。
code:bram_controller.sv
// メモリの初期化
initial begin
// LED へ 0b1001_1001 を表示する
mem0 = addi(1, 0, 8'b10011001); // x1 = 0b10011001 mem1 = lui(2, 32'hf0001000 >> 12); // x2 = 0xf0001000 mem2 = sw(2, 1, 0); // Mx2+0 = x1 mem3 = jal(0, 0); // jal x0, 0 (無限ループ) end
動かしてみる。LEDにちゃんと 1001_1001 が表示されている。
https://gyazo.com/fdf84b67d0635acb07b3145f31546fca
もうちょっと複雑なプログラムを動かしてみる
code:bram_controller.sv
// メモリの初期化
initial begin
// デバッグ用の各種変数
mem0 = lui(10, 32'hf0001000 >> 12); // x10 = 0xf0001000 //----------------------------------------
// 1 から 10 までの合計を LED 出力するプログラム
//----------------------------------------
mem6 = sw(10, 11, 0); // LED に 1 を点灯 mem7 = addi(1, 0, 0); // sum = 0 mem8 = addi(2, 0, 10); // n = 10 // loop:
mem9 = sw(10, 12, 0); // LED に 2 を点灯 mem10 = beq(2, 0, 8); // if (n == 0) break mem11 = add(1, 1, 2); // sum = sum + n mem12 = addi(2, 2, -1); // n = n - 1 mem13 = jal(0, -8); // jump loop // break:
mem14 = sw(10, 13, 0); // LED に 3 を点灯 mem15 = sw(10, 1, 0); // Mx2+0 = x1 mem16 = add(0, 0, 0); // nop mem17 = jal(0, 0); // jal x0, 0 (無限ループ) // デバッグ
// mem20 = jal(0, 0); // 無限ループ // mem24 = jal(0, 0); // 無限ループ end
1から10までの合計 55 がLEDに出力されている
https://gyazo.com/23c912a02d920139ab2e64361a782ae8