概要
micropythonでshrike-lite、やってみた。
shrikeで、自作cpuやってみた。
UUUをuartで返す。
8bit CPUの仕様
stackマシーン
stackは、8bitで4個搭載
RAMはゼロ。
命令は、12bit
ROMは、64個
RAMは、0個
プログラムカウンタ(pc) 6bit 1個
アドレスレジスタ 6bit 1個
outレジスタ 8bit 1個
出力は、uart
機械語
| ニーモニック | 動作 | 機械語 |
|---|---|---|
| end | 停止 | 000 |
| push x | スタックに数値xxを積む。 | 2xx |
| adr x | アドレスADRに数値xxを代入。 | 3xx |
| ifeq | スタックTOPが真ならアドレスADRに飛ぶ。 | 400 |
| ifne | スタックTOPが偽ならアドレスADRに飛ぶ。 | 401 |
| jmp | アドレスADRに飛ぶ。 | 4FF |
| out | スタックTOPを数値出力。 | 500 |
| emit | スタックTOPを出力。 | 600 |
| + | スタックTOPをNEXTで加算。 | F00 |
| - | スタックTOPをNEXTで減算。 | F01 |
| * | スタックTOPをNEXTで乗算。 | F02 |
| / | スタックTOPをNEXTで除算。 | F03 |
| % | スタックTOPをNEXTで剰余。 | F04 |
| = | スタックTOPをNEXTでイコール。 | F0F |
| < | スタックTOPをNEXTで小さい。 | F10 |
| > | スタックTOPをNEXTで大きい。 | F11 |
| dup | スタックTOPを複写。 | F20 |
| drop | スタックTOPを廃棄。 | F21 |
手順
投入するプログラム
UUUを繰り返し、印字する。
0: push 'U'
1: emit
2: push 'U'
3: emit
4: push 'U'
5: emit
6: push '\r'
7: emit
8: push '\n'
9: emit
10: adr 0
11: jmp
top.vを、書く。
top.v
(* top *) module top #(parameter CLK = 50_000_000, parameter BAUD_RATE = 115200) (
(* iopad_external_pin, clkbuf_inhibit *) input clk,
(* iopad_external_pin *) output clk_en,
(* iopad_external_pin *) input rst,
(* iopad_external_pin *) input rx,
(* iopad_external_pin *) output tx,
(* iopad_external_pin *) output tx_en,
(* iopad_external_pin *) output led,
(* iopad_external_pin *) output led_en
);
localparam CNT_MS = CLK / 200 - 1;
reg [31:0] timer_cnt;
reg timer_done;
reg [3:0] state = 4'd0;
reg flag = 1'b0;
reg [7:0] q0,
q1,
q2,
q3;
reg [6:0] pc;
reg [6:0] adr;
reg [7:0] out;
reg [11:0] code;
wire tx_busy;
wire [3:0] op = code[11:8];
wire [7:0] opland = code[7:0];
assign clk_en = 1'b1;
assign tx_en = 1'b1;
assign led_en = 1'b1;
assign led = flag;
uart_tx #(.CLK_FREQ(CLK), .BAUD_RATE(BAUD_RATE))
ut (.clk(clk), .rst(rst), .tx_start(flag), .tx_data(out), .tx(tx), .tx_busy(tx_busy));
initial
begin
$monitor("pc: %d busy: %d out: %s flag: %d", pc, tx_busy, out, flag);
end
always @(*)
begin
case (pc)
7'd0: code = 12'h2_55; // PUSH 'U'
7'd1: code = 12'h6_00; // emit
7'd2: code = 12'h2_55; // PUSH 'U'
7'd3: code = 12'h6_00; // emit
7'd4: code = 12'h2_55; // PUSH 'U'
7'd5: code = 12'h6_00; // emit
7'd6: code = 12'h2_0D; // PUSH 0D (\r)
7'd7: code = 12'h6_00; // emit
7'd8: code = 12'h2_0A; // PUSH 0A (\n)
7'd9: code = 12'h6_00; // emit
7'd10: code = 12'h3_00; // ADR 0
7'd11: code = 12'h4_FF; // JMP
default: code = 12'h0_00;
endcase
end
always @(posedge clk or posedge rst)
begin
if (rst)
begin
timer_cnt <= 32'd0;
timer_done <= 1'b0;
end
else
begin
if (timer_cnt >= CNT_MS)
begin
timer_cnt <= 32'd0;
timer_done <= 1'b1;
end
else
begin
timer_cnt <= timer_cnt + 1'b1;
timer_done <= 1'b0;
end
end
end
always @(posedge clk or posedge rst)
begin
if (rst)
begin
q0 <= 8'd0;
q1 <= 8'd0;
q2 <= 8'd0;
q3 <= 8'd0;
adr <= 0;
out <= 0;
pc <= 0;
state <= 4'd0;
flag <= 1'b0;
end
else
begin
case (op)
4'h0: // HALT
pc <= pc;
4'h2: // PUSH
begin
q3 <= q2; q2 <= q1; q1 <= q0;
q0 <= opland;
pc <= pc + 1;
end
4'h3: // ADR
begin
adr <= opland[6:0];
pc <= pc + 1;
end
4'h4: // IF
begin
case (opland)
8'h00: // JEQ
begin
if (q0 == 8'd0)
pc <= adr;
else
pc <= pc + 1;
q0 <= q1; q1 <= q2; q2 <= q3; q3 <= 8'd0;
end
8'h01: // JNE
begin
if (q0 == 8'd1)
pc <= adr;
else
pc <= pc + 1;
q0 <= q1; q1 <= q2; q2 <= q3; q3 <= 8'd0;
end
8'hFF: // JMP
pc <= adr;
default:
pc <= pc + 1;
endcase
end
4'h5: // OUT
begin
case (state)
4'h0:
begin
if (q0 > 9)
begin
out <= q0 / 10 + 48;
flag <= 1'b1;
state <= 4'h1;
end
else
begin
state <= 4'h3;
end
end
4'h1:
begin
if (tx_busy)
begin
flag <= 1'b0;
state <= 4'h2;
end
end
4'h2:
begin
if (!tx_busy)
begin
state <= 4'h3;
end
end
4'h3:
begin
out <= q0 % 10 + 48;
q0 <= q1; q1 <= q2; q2 <= q3; q3 <= 8'd0;
flag <= 1'b1;
state <= 4'h4;
end
4'h4:
begin
if (tx_busy)
begin
flag <= 1'b0;
state <= 4'h5;
end
end
4'h5:
begin
if (!tx_busy)
begin
state <= 4'h6;
end
end
4'h6:
begin
if (timer_done)
begin
pc <= pc + 1;
state <= 4'h0;
end
end
default:
state <= 4'h0;
endcase
end
4'h6: // emit
begin
case (state)
4'h0:
begin
out <= q0;
q0 <= q1; q1 <= q2; q2 <= q3; q3 <= 8'd0;
flag <= 1'b1;
state <= 4'h1;
end
4'h1:
begin
if (tx_busy)
begin
flag <= 1'b0;
state <= 4'h2;
end
end
4'h2:
begin
if (!tx_busy)
begin
state <= 4'h3;
end
end
4'h3:
begin
if (timer_done)
begin
pc <= pc + 1;
state <= 4'h0;
end
end
default:
state <= 4'h0;
endcase
end
4'hF:
begin
case (opland)
8'h00: begin q0 <= q1 + q0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // +
8'h01: begin q0 <= q1 - q0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // -
//8'h02: begin q0 <= q1 * q0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // *
//8'h03: begin q0 <= (q0 != 0) ? q1 / q0 : 8'd0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // /
8'h04: begin q0 <= (q0 != 0) ? q1 % q0 : 8'd0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // mod
8'h0F: begin q0 <= (q1 == q0) ? 8'd1 : 8'd0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // =
//8'h10: begin q0 <= (q1 < q0) ? 8'd1 : 8'd0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // <
//8'h11: begin q0 <= (q1 > q0) ? 8'd1 : 8'd0; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // >
8'h20: begin q3 <= q2; q2 <= q1; q1 <= q0; end // dup
8'h21: begin q0 <= q1; q1 <= q2; q2 <= q3; q3 <= 8'd0; end // drop
default: ;
endcase
pc <= pc + 1;
end
default:
pc <= pc + 1;
endcase
end
end
endmodule
テストベンチ結果
Resource Report
#### Resource Utilization Report ####
Top level module name: top
Resource usage:
CLB LUT5s: 306/1120 (27.32%)
Lut5: 306 (27.32%)
Lut5 for Shift-register: 0 (0.00%)
Lut5 for Distributed memory: 0 (0.00%)
FFs: 112
CLB FFs: 110/1120 (9.82%)
IOB FFs: 2/736 (0.27%)
CLBs: 51/140 (36.43%)
Tiles: 1/1 (100.00%)
RTL Input ports: 2
RTL Output ports: 5
GPIOs: 3/19 (15.79%)
PLLs: 0/1 (0.00%)
OSCs: 1/1 (100.00%)
4k BRAMs: 0/8 (0.00%)
1 Clock
Clock 0: net 276, clk, Fanout: 44 CLBs
Total 1 unique clocks
#### Resource Utilization Complete ####
TILE-REQ: 0 0 0 1
### Total FPGA resources from current floorplan: ###
184 IOBs
140 Logic 6-LUT CLBs
40 CLBs support RAM/SR
Usage of Logic CLBs is 36.4%.
Logic-Memory CLB for RAM/Shift-Register is 0.0%.
Usage of LUTs is 35.9%.
dual-LUTs is 18.9%.
Usage of FF is 16.8%.
dual-FF is 2.9%.
Usage of IO CLB is 2.2%.
main.pyを書く。
from machine import UART, Pin
import time
import shrike
import uselect
shrike.reset()
shrike.flash("fizz3.bin")
reset_pin = Pin(2, Pin.OUT, value = 0)
uart = UART(0, baudrate = 115200, tx = Pin(0), rx = Pin(1), rxbuf = 512)
print("port open")
reset_pin.value(1)
time.sleep(0.5)
reset_pin.value(0)
print("reset")
def send(value):
uart.write(bytes([value]))
def reset():
print("reset")
reset_pin.value(1)
time.sleep(0.5)
reset_pin.value(0)
def resv():
if uart.any():
reply = uart.read(1)[0]
print(reply, chr(reply))
def line():
while True:
time.sleep(0.1)
if uart.any():
reply = uart.readline()
print(reply)
def test():
while True:
time.sleep(0.01)
if uart.any():
reply = uart.read(1)[0]
print(chr(reply), end="")
実行結果
>shrike0
Port open ok
[shrike_flash] FPGA reset done
[shrike_flash] FPGA reset done Starting FPGA flash...
[shrike_fpga] flashing: fizz3.bin
[shrike_flash] FPGA programming done.
port open
reset
MicroPython v1.26.0-preview.527.g599f545a3.dirty on 2025-10-11; Vicharak Shrike FPGA with RP2040
Type "help()" for more information.
test()
>>> test()
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
UUU
2週間、詰んで、学んだこと。
- micropythonが、処理が追い付かない。fpgaでdelay入れる。5MHz/200で送ってる。
- fpgaが、これ以上、ロジック増やすと入らない。フルスペックのCPUが書けない。Bramに逃がすと、どうなるか。試さないといけない。
以上。
