Download rtl/src/subleq_cpu.sv from Snapkitty/flash-attention-rtl: direct link, hf CLI and curl.
- Browser
- Download file 5.92 kB
-
https://huggingface.co/Snapkitty/flash-attention-rtl/resolve/main/rtl/src/subleq_cpu.sv
- Command line
-
hf download hf://Snapkitty/flash-attention-rtl/rtl/src/subleq_cpu.sv
-
curl -L -o subleq_cpu.sv https://huggingface.co/Snapkitty/flash-attention-rtl/resolve/main/rtl/src/subleq_cpu.sv
5.92 kB
| // SUBLEQ CPU with a FlashAttention trap. | |
| // | |
| // Instruction = triad (a, b, c) of 32-bit words fetched from pc, pc+1, pc+2: | |
| // a == -2 FA trap: b = address of the 6-word FA_BEGIN descriptor, c = continuation. | |
| // The FA engine takes the memory bus, writes O to memory, then execution | |
| // resumes at c (pc <- c; negative c halts). | |
| // a == -1 input: mem[b] <- next input word, pc <- pc + 3 | |
| // a < -2 fault | |
| // b < 0 output: emit mem[a], pc <- pc + 3 | |
| // otherwise mem[b] <- mem[b] - mem[a]; pc <- (mem[b] <= 0) ? c : pc + 3 | |
| // pc < 0 halts. Any address outside [0, MEM_WORDS) faults. | |
| // | |
| // This is the same instruction semantics as runSubleq() in subleq_bf.nim (32-bit words here). | |
| module subleq_cpu | |
| import fa_pkg::*; | |
| #( | |
| parameter int MEM_WORDS = 4096 | |
| ) ( | |
| input logic clk, | |
| input logic rst_n, | |
| // memory master (request held until ready) | |
| output logic mem_req, | |
| output logic mem_we, | |
| output logic [31:0] mem_addr, | |
| output logic [31:0] mem_wdata, | |
| input logic [31:0] mem_rdata, | |
| input logic mem_ready, | |
| // word I/O | |
| output logic io_out_valid, // 1-cycle pulse | |
| output logic signed [31:0] io_out_data, | |
| input logic signed [31:0] io_in_data, // current input word | |
| output logic io_in_pop, // 1-cycle pulse: input word consumed | |
| // FA engine | |
| output logic fa_start, // 1-cycle pulse | |
| output logic [31:0] fa_desc_addr, | |
| output logic fa_active, // CPU is waiting on the engine (it owns the bus) | |
| input logic fa_done, | |
| input logic fa_err, | |
| output logic halted, | |
| output logic fault, | |
| output logic [31:0] steps | |
| ); | |
| typedef enum logic [3:0] { | |
| C_FETCH_A, C_FETCH_B, C_FETCH_C, C_DECODE, | |
| C_RD_A, C_RD_B, C_WR_B, C_OUT, C_IN_WR, | |
| C_FA_START, C_FA_WAIT, C_NEXT, C_HALT, C_FAULT | |
| } state_t; | |
| state_t state; | |
| logic signed [31:0] pc, a, b, c, ma, mb, res, npc; | |
| localparam logic signed [31:0] LIM = 32'(MEM_WORDS); | |
| function automatic logic in_mem(input logic signed [31:0] x); | |
| return (x >= 0) && (x < LIM); | |
| endfunction | |
| assign halted = (state == C_HALT); | |
| assign fault = (state == C_FAULT); | |
| assign fa_active = (state == C_FA_START) || (state == C_FA_WAIT); | |
| assign fa_start = (state == C_FA_START); | |
| assign fa_desc_addr = b; | |
| assign io_out_valid = (state == C_OUT); | |
| assign io_out_data = ma; | |
| assign io_in_pop = (state == C_IN_WR) && mem_ready; | |
| always_comb begin | |
| mem_req = 1'b0; | |
| mem_we = 1'b0; | |
| mem_addr = '0; | |
| mem_wdata = '0; | |
| case (state) | |
| C_FETCH_A: begin mem_req = 1'b1; mem_addr = 32'(pc); end | |
| C_FETCH_B: begin mem_req = 1'b1; mem_addr = 32'(pc + 32'sd1); end | |
| C_FETCH_C: begin mem_req = 1'b1; mem_addr = 32'(pc + 32'sd2); end | |
| C_RD_A: begin mem_req = 1'b1; mem_addr = 32'(a); end | |
| C_RD_B: begin mem_req = 1'b1; mem_addr = 32'(b); end | |
| C_WR_B: begin mem_req = 1'b1; mem_we = 1'b1; mem_addr = 32'(b); mem_wdata = 32'(res); end | |
| C_IN_WR: begin mem_req = 1'b1; mem_we = 1'b1; mem_addr = 32'(b); mem_wdata = 32'(io_in_data); end | |
| default: ; | |
| endcase | |
| end | |
| always_ff @(posedge clk or negedge rst_n) begin | |
| if (!rst_n) begin | |
| state <= C_FETCH_A; | |
| pc <= '0; a <= '0; b <= '0; c <= '0; ma <= '0; mb <= '0; res <= '0; npc <= '0; | |
| steps <= '0; | |
| end else begin | |
| case (state) | |
| C_FETCH_A: begin | |
| if (!in_mem(pc) || !in_mem(pc + 32'sd2)) state <= C_FAULT; | |
| else if (mem_ready) begin a <= mem_rdata; state <= C_FETCH_B; end | |
| end | |
| C_FETCH_B: if (mem_ready) begin b <= mem_rdata; state <= C_FETCH_C; end | |
| C_FETCH_C: if (mem_ready) begin c <= mem_rdata; state <= C_DECODE; end | |
| C_DECODE: begin | |
| if (a == -32'sd1) begin | |
| state <= in_mem(b) ? C_IN_WR : C_FAULT; | |
| end else if (a == 32'(FA_TRAP)) begin | |
| state <= (in_mem(b) && in_mem(b + 32'sd5)) ? C_FA_START : C_FAULT; | |
| end else if (a < 0) begin | |
| state <= C_FAULT; | |
| end else if (b < 0) begin | |
| state <= in_mem(a) ? C_RD_A : C_FAULT; | |
| end else begin | |
| state <= (in_mem(a) && in_mem(b)) ? C_RD_A : C_FAULT; | |
| end | |
| end | |
| C_RD_A: if (mem_ready) begin | |
| ma <= mem_rdata; | |
| state <= (b < 0) ? C_OUT : C_RD_B; | |
| end | |
| C_RD_B: if (mem_ready) begin mb <= mem_rdata; res <= $signed(mem_rdata) - ma; state <= C_WR_B; end | |
| C_WR_B: if (mem_ready) begin | |
| npc <= (res <= 0) ? c : pc + 32'sd3; | |
| state <= C_NEXT; | |
| end | |
| C_OUT: begin npc <= pc + 32'sd3; state <= C_NEXT; end | |
| C_IN_WR: if (mem_ready) begin npc <= pc + 32'sd3; state <= C_NEXT; end | |
| C_FA_START: state <= C_FA_WAIT; | |
| C_FA_WAIT: if (fa_done) begin | |
| npc <= c; | |
| state <= fa_err ? C_FAULT : C_NEXT; | |
| end | |
| C_NEXT: begin | |
| pc <= npc; | |
| steps <= steps + 32'd1; | |
| state <= (npc < 0) ? C_HALT : C_FETCH_A; | |
| end | |
| default: ; // C_HALT, C_FAULT are terminal | |
| endcase | |
| end | |
| end | |
| endmodule : subleq_cpu | |