cpu32

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit f6f9132373295fa8b9dd5a53d3f2acb1464374da
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat,  4 Feb 2012 22:46:59 -0800

initial commit

Diffstat:
AMakefile | 17+++++++++++++++++
Aa32.c | 583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aalu.v | 34++++++++++++++++++++++++++++++++++
Acpu32.v | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aisa.txt | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibrary.v | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aram.v | 20++++++++++++++++++++
Aregfile.v | 32++++++++++++++++++++++++++++++++
Arom.asm | 20++++++++++++++++++++
Arom.v | 22++++++++++++++++++++++
Atestbench.v | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 1122 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,17 @@ +# Copyright 2012, Brian Swetland. Use at your own risk. + +SRC := testbench.v cpu32.v alu.v ram.v rom.v regfile.v library.v + +all: a32 testbench + +testbench: $(SRC) rom.txt + iverilog -o testbench $(SRC) + +rom.txt: rom.asm + ./a32 rom.asm rom.txt + +a32: a32.c + gcc -g -Wall -o a32 a32.c + +clean:: + rm -f testbench testbench.vcd a32 rom.txt diff --git a/a32.c b/a32.c @@ -0,0 +1,583 @@ +// Copyright 2009-2012, Brian Swetland. Use at your own risk. + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <strings.h> +#include <string.h> + +static unsigned linenumber = 0; +static char linestring[256]; +static char *filename; + +FILE *ofp = 0; + +void die(const char *fmt, ...) { + va_list ap; + fprintf(stderr,"%s:%d: ", filename, linenumber); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr,"\n"); + if (linestring[0]) + fprintf(stderr,"%s:%d: >> %s <<\n", filename, linenumber, linestring); + exit(1); +} + +int is_signed_16(unsigned n) { + if (n < 0xFFFF) + return 1; + if ((n & 0xFFFF0000) == 0xFFFF0000) + return 1; + return 0; +} + +unsigned rom[65535]; +unsigned PC = 0; + +struct fixup { + struct fixup *next; + unsigned pc; + unsigned type; // 16, 24 +}; + +struct label { + struct label *next; + struct fixup *fixups; + const char *name; + unsigned pc; + unsigned defined; +}; + +struct label *labels; +struct fixup *fixups; + +void fixup_branch(const char *name, int addr, int btarget, int type) { + unsigned n; + + n = btarget - addr; + + if (!is_signed_16(n)) { + die("label '%s' at %08x is out of range of %08x\n", + name, btarget, addr); + } + rom[addr] = (rom[addr] & 0xFFFF0000) | (n & 0xFFFF); +} + +void setlabel(const char *name, unsigned pc) { + struct label *l; + struct fixup *f; + + for (l = labels; l; l = l->next) { + if (!strcasecmp(l->name, name)) { + if (l->defined) die("cannot redefine '%s'", name); + l->pc = pc; + l->defined = 1; + for (f = l->fixups; f; f = f->next) { + fixup_branch(name, f->pc, l->pc, f->type); + } + return; + } + } + l = malloc(sizeof(*l)); + l->name = strdup(name); + l->pc = pc; + l->fixups = 0; + l->defined = 1; + l->next = labels; + labels = l; +} + +void uselabel(const char *name, unsigned pc, unsigned type) { + struct label *l; + struct fixup *f; + + for (l = labels; l; l = l->next) { + if (!strcasecmp(l->name, name)) { + if (l->defined) { + fixup_branch(name, pc, l->pc, type); + return; + } else { + goto add_fixup; + } + } + } + l = malloc(sizeof(*l)); + l->name = strdup(name); + l->pc = 0; + l->fixups = 0; + l->defined = 0; + l->next = labels; + labels = l; +add_fixup: + f = malloc(sizeof(*f)); + f->pc = pc; + f->type = type; + f->next = l->fixups; + l->fixups = f; +} + +void checklabels(void) { + struct label *l; + for (l = labels; l; l = l->next) { + if (!l->defined) { + die("undefined label '%s'", l->name); + } + } +} + +void disassemble(char *buf, unsigned pc, unsigned instr); + +void emit(unsigned instr) { + rom[PC++] = instr; +} + +void save(const char *fn) { + unsigned n; + char dis[128]; + FILE *fp = fopen(fn, "w"); + if (!fp) die("cannot write to '%s'", fn); + for (n = 0; n < PC; n++) { + disassemble(dis, n, rom[n]); + fprintf(fp, "%08x // %08x: %s\n", rom[n], n, dis); + } + fclose(fp); +} + +#define MAXTOKEN 32 + +enum tokens { + tEOL, + tCOMMA, tCOLON, tOBRACK, tCBRACK, tDOT, + tSTRING, + tNUMBER, + tORR, tAND, tADD, tSUB, tSHL, tSHR, tXOR, tTBS, + tSEQ, tSLT, tSGT, tREV, tBIS, tBIC, tMOV, tMHI, + tB, tBL, tBZ, tBNZ, tBLZ, tBLNZ, tLW, tSW, + tR0, tR1, tR2, tR3, tR4, tR5, tR6, tR7, + rR8, tR9, tR10, tR11, tR12, tR13, tR14, tR15, + tSP, tLR, + tNOP, + tEQU, + NUMTOKENS, +}; + +char *tnames[] = { + "<EOL>", + ",", ":", "[", "]", ".", + "<STRING>", + "<NUMBER>", + "ORR", "AND", "ADD", "SUB", "SHL", "SHR", "XOR", "TBS", + "SEQ", "SLT", "SGT", "REV", "BIS", "BIC", "MOV", "MHI", + "B", "BL", "BZ", "BNZ", "BLZ", "BLNZ", "LW", "SW", + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "SP", "LR", + "NOP", + "EQU", +}; + +#define FIRST_ALU_OP tORR +#define LAST_ALU_OP tMHI +#define FIRST_REGISTER tR0 +#define LAST_REGISTER tR15 + +int is_register(unsigned tok) { + return ((tok >= FIRST_REGISTER) && (tok <= LAST_REGISTER)); +} + +int is_alu_op(int token) { + return ((token >= FIRST_ALU_OP) && (token <= LAST_ALU_OP)); +} + +unsigned to_register(unsigned tok) { + if (!is_register(tok)) die("not a register (%s)", tnames[tok]); + return tok - FIRST_REGISTER; +} + +int is_stopchar(unsigned x) { + switch (x) { + case 0: + case ' ': + case '\t': + case '\r': + case '\n': + case ',': + case ':': + case '[': + case ']': + case '.': + return 1; + default: + return 0; + } +} + +int tokenize(char *line, unsigned *tok, unsigned *num, char **str) { + char *s; + int count = 0; + unsigned x, n, neg; + linenumber++; + + for (;;) { + x = *line; + again: + if (count == 31) die("line too complex"); + + switch (x) { + case 0: + goto alldone; + case ' ': + case '\t': + case '\r': + case '\n': + line++; + continue; + case '/': + if (line[1] == '/') + goto alldone; + + case ',': + str[count] = ","; + num[count] = 0; + tok[count++] = tCOMMA; + line++; + continue; + case ':': + str[count] = ":"; + num[count] = 0; + tok[count++] = tCOLON; + line++; + continue; + case '[': + str[count] = "["; + num[count] = 0; + tok[count++] = tOBRACK; + line++; + continue; + case ']': + str[count] = "]"; + num[count] = 0; + tok[count++] = tCBRACK; + line++; + continue; + case '.': + str[count] = "."; + num[count] = 0; + tok[count++] = tDOT; + line++; + continue; + } + + s = line++; + while (!is_stopchar(*line)) line++; + + /* save the stopchar */ + x = *line; + *line = 0; + + neg = (s[0] == '-'); + if (neg && isdigit(s[1])) s++; + + str[count] = s; + if (isdigit(s[0])) { + num[count] = strtoul(s, 0, 0); + if(neg) num[count] = -num[count]; + tok[count++] = tNUMBER; + goto again; + } + if (isalpha(s[0])) { + num[count] = 0; + for (n = tNUMBER + 1; n < NUMTOKENS; n++) { + if (!strcasecmp(s, tnames[n])) { + str[count] = tnames[n]; + tok[count++] = n; + goto again; + } + } + + while (*s) { + if (!isalnum(*s) && (*s != '_')) + die("invalid character '%c' in identifier", *s); + s++; + } + tok[count++] = tSTRING; + goto again; + } + die("invalid character '%c'", s[0]); + } + +alldone: + str[count] = ""; + num[count] = 0; + tok[count++] = tEOL; + return count; +} + + +void expect(unsigned expected, unsigned got) { + if (expected != got) + die("expected %s, got %s", tnames[expected], tnames[got]); +} + +void expect_register(unsigned got) { + if (!is_register(got)) + die("expected register, got %s", tnames[got]); +} + +#define REG(n) (tnames[FIRST_REGISTER + (n)]) + +void disassemble(char *buf, unsigned pc, unsigned instr) { + unsigned op = instr >> 28; + unsigned fn = (instr >> 24) & 0xF; + unsigned opfn = (instr >> 24) & 0xFF; + unsigned a = (instr >> 20) & 0xF; + unsigned b = (instr >> 16) & 0xF; + unsigned d = (instr >> 12) & 0xF; + unsigned i16 = instr & 0xFFFF; + short s16 = i16; + + /* check for special forms */ + if (instr == 0) { + sprintf(buf, "NOP"); + return; + } + + switch (opfn) { + case 0x0B: + case 0x1B: + sprintf(buf, "REV %s, %s", REG(d), REG(a)); + break; + case 0x0E: + sprintf(buf, "MOV %s, %s", REG(d), REG(b)); + break; + case 0x1E: + sprintf(buf, "MOV %s, #%d", REG(d), i16); + break; + case 0x0F: + sprintf(buf, "MHI %s, %s", REG(d), REG(b)); + break; + case 0x1F: + sprintf(buf, "MHI %s, #%d", REG(d), i16); + break; + case 0x20: + sprintf(buf, "LW %s, [%s, #%d]", REG(b), REG(a), i16); + break; + case 0x30: + sprintf(buf, "SW %s, [%s, #%d]", REG(b), REG(a), i16); + break; + case 0x41: + sprintf(buf, "BZ %s, 0x%08x", REG(a), (pc + s16)); + break; + case 0x42: + sprintf(buf, "BNZ %s, 0x%08x", REG(a), (pc + s16)); + break; + case 0x43: + sprintf(buf, "B 0x%08x", (pc + s16)); + break; + case 0x49: + sprintf(buf, "BLZ %s, 0x%08x", REG(a), (pc + s16)); + break; + case 0x4A: + sprintf(buf, "BLNZ %s, 0x%08x", REG(a), (pc + s16)); + break; + case 0x4B: + sprintf(buf, "BL 0x%08x", (pc + s16)); + break; + default: + if (op == 0) { + sprintf(buf, "%-5s%s, %s, %s", + tnames[FIRST_ALU_OP + fn], REG(d), REG(a), REG(b)); + } else if (op == 1) { + sprintf(buf, "%-5s%s, %s, #%d", + tnames[FIRST_ALU_OP + fn], REG(b), REG(a), i16); + return; + } else { + sprintf(buf, "UND 0x%04x", opfn); + } + } +} + +#define TO_A(n) (((n) & 0xF) << 20) +#define TO_B(n) (((n) & 0xF) << 16) +#define TO_D(n) (((n) & 0xF) << 12) +#define TO_I16(n) ((n) & 0xFFFF) + +void assemble_line(int n, unsigned *tok, unsigned *num, char **str) { + unsigned instr = 0; + unsigned tmp; + + if (tok[0] == tSTRING) { + if (tok[1] == tCOLON) { + setlabel(str[0],PC); + tok+=2; + num+=2; + str+=2; + n-=2; + } else { + die("unexpected identifier '%s'", str[0]); + } + } + + switch(tok[0]) { + case tEOL: + /* blank lines are fine */ + return; + case tNOP: + emit(0x00000000); + return; + case tMOV: + expect_register(tok[1]); + expect(tCOMMA,tok[2]); + expect(tNUMBER,tok[3]); + emit(0x1E000000 | TO_B(to_register(tok[1])) | TO_I16(num[3])); + if (num[3] & 0xFFFF0000) + emit(0x1F000000 | TO_B(to_register(tok[1])) | (TO_I16(num[3] >> 16))); + return; + case tMHI: + expect_register(tok[1]); + expect(tCOMMA,tok[2]); + expect(tNUMBER,tok[3]); + emit(0x1F000000 | TO_B(to_register(tok[1])) | TO_I16(num[3])); + return; + case tB: + case tBL: + if (tok[0] == tB) { + instr = 0x43000000; + } else { + instr = 0x4B000000; + } + if (tok[1] == tSTRING) { + emit(instr); + uselabel(str[1], PC - 1, 16); + } else if ((tok[1] == tNUMBER) || (tok[1] == tDOT)) { + if (!is_signed_16(num[1])) die("branch target out of range"); + emit(instr | TO_I16(num[1])); + } else { + die("expected branch target, got %s", tnames[tok[1]]); + } + return; + case tBNZ: + case tBZ: + case tBLNZ: + case tBLZ: + switch (tok[0]) { + case tBZ: instr = 0x41000000; break; + case tBNZ: instr = 0x42000000; break; + case tBLZ: instr = 0x49000000; break; + case tBLNZ: instr = 0x4A000000; break; + } + expect_register(tok[1]); + expect(tCOMMA,tok[2]); + instr |= TO_A(to_register(tok[1])); + if (tok[3] == tSTRING) { + emit(instr); + uselabel(str[3], PC - 1, 16); + } else if ((tok[3] == tNUMBER) || (tok[3] == tDOT)) { + if (!is_signed_16(num[3])) die("branch target out of range"); + emit(instr | TO_I16(num[3])); + } else { + die("expected branch target, got %s", tnames[tok[1]]); + } + return; + case tLW: + case tSW: + if (tok[0] == tLW) { + instr = 0x20000000; + } else { + instr = 0x30000000; + } + expect_register(tok[1]); + expect(tCOMMA,tok[2]); + expect(tOBRACK,tok[3]); + expect_register(tok[4]); + if (tok[5] == tCOMMA) { + expect(tNUMBER, tok[6]); + expect(tCBRACK, tok[7]); + tmp = num[6]; + } else { + expect(tCBRACK, tok[5]); + tmp = 0; + } + if (!is_signed_16(tmp)) die("index too large"); + instr |= TO_B(to_register(tok[1])) | TO_A(to_register(tok[4]) | TO_I16(tmp)); + emit(instr); + return; + } + if (is_alu_op(tok[0])) { + expect_register(tok[1]); + expect(tok[2],tCOMMA); + expect_register(tok[3]); + expect(tok[4],tCOMMA); + + instr = ((tok[0] - FIRST_ALU_OP) << 24) | TO_A(tok[3]); + + if (is_register(tok[5])) { + emit(instr | TO_B(to_register(tok[5])) | TO_D(to_register(tok[1]))); + } else if (tok[5] == tNUMBER) { + if (num[5] > 65535) die("immediate too large"); + emit(instr | 0x10000000 | TO_B(to_register(tok[1])) | TO_I16(num[5])); + } else { + die("expected register or #, got %s", tnames[tok[5]]); + } + return; + } + + die("HUH"); + +} + +void assemble(const char *fn) +{ + FILE *fp; + char line[256]; + int n; + + unsigned tok[MAXTOKEN]; + unsigned num[MAXTOKEN]; + char *str[MAXTOKEN]; + char *s; + + fp = fopen(fn, "r"); + if (!fp) die("cannot open '%s'", fn); + + while (fgets(line, sizeof(line)-1, fp)) { + strcpy(linestring, line); + s = linestring; + while (*s) { + if ((*s == '\r') || (*s == '\n')) *s = 0; + else s++; + } + n = tokenize(line, tok, num, str); +#if DEBUG + { + int i + printf("%04d: (%02d) ", linenumber, n); + for (i = 0; i < n; i++) + printf("%s ", tnames[tok[i]]); + printf("\n"); + } +#endif + assemble_line(n, tok, num, str); + } +} + + +int main(int argc, char **argv) +{ + const char *outname = "out.hex"; + filename = argv[1]; + + if (argc < 2) + die("no file specified"); + if (argc == 3) + outname = argv[2]; + + assemble(filename); + linestring[0] = 0; + checklabels(); + save(outname); + + return 0; +} diff --git a/alu.v b/alu.v @@ -0,0 +1,34 @@ +// CPU32 ALU +// +// Copyright 2012, Brian Swetland. Use at your own risk. + +module alu ( + input [3:0] opcode, + input [31:0] left, + input [31:0] right, + output reg [31:0] out + ); + +wire [31:0] rbit; +assign rbit = (1 << right[4:0]); + +always @ (*) + case (opcode) + 4'b0000: out <= (left | right); + 4'b0001: out <= (left & right); + 4'b0010: out <= (left + right); + 4'b0011: out <= (left - right); + 4'b0100: out <= (left << right[4:0]); + 4'b0101: out <= (left >> right[4:0]); + 4'b0110: out <= (left ^ right); + 4'b0111: out <= (left & rbit) ? 1 : 0; + 4'b1000: out <= (left == right) ? 1 : 0; + 4'b1001: out <= (left < right) ? 1 : 0; + 4'b1010: out <= (left > right) ? 1 : 0; + 4'b1011: out <= 0; + 4'b1100: out <= (left | rbit); + 4'b1101: out <= (left & ~rbit); + 4'b1110: out <= right; + 4'b1111: out <= { right[15:0], left[15:0] }; + endcase +endmodule diff --git a/cpu32.v b/cpu32.v @@ -0,0 +1,115 @@ +// CPU32 Non-pipelined Core +// +// Copyright 2012, Brian Swetland. Use at your own risk. + +module cpu32 ( + input clk, + output [31:0] i_addr, + input [31:0] i_data, + output [31:0] d_addr, + output [31:0] d_data_w, + input [31:0] d_data_r, + output d_we + ); + +wire [31:0] ir, pc; + +wire [31:0] next_pc, pc_adjust; + +wire [3:0] opcode, opfunc, opsela, opselb, opseld; +wire [15:0] opimm16; + +wire [31:0] adata, bdata, wdata, result; +wire [3:0] alu_wsel; + +assign opcode = ir[31:28]; +assign opfunc = ir[27:24]; +assign opsela = ir[23:20]; +assign opselb = ir[19:16]; +assign opseld = ir[15:12]; +assign opimm16 = ir[15:0]; + +wire ctl_regs_we; // 1 = write back to register file +wire ctl_d_or_b; // 0 = write to R[opseld], 1 = R[opselb] +wire ctl_branch; // 1 = immediate branch +wire ctl_ram_op; +wire ctl_imm16; // 0 = bdata, 1 = imm16 -> alu right + +wire [3:0] ctl_alu_func; + +// cheesy decoder -- TODO: write for real +assign ctl_regs_we = ((opcode[3:1] == 0) || (opcode == 2)); +assign ctl_d_or_b = ((opcode == 1) || (opcode == 2)); +assign ctl_branch = (opcode == 4); +assign ctl_ram_rd = (opcode == 2); +assign ctl_ram_we = (opcode == 3); +assign ctl_ram_op = ((opcode == 2) || (opcode == 3)); +assign ctl_alu_func = ctl_ram_op ? 4'b0010 : opfunc; +assign ctl_imm16 = (opcode != 0); + +register #(32) PC ( + .clk(clk), + .en(1), + .din(next_pc), + .dout(pc) + ); + +regfile #(32,4) REGS ( + .clk(clk), + .we(ctl_regs_we), + .wsel(alu_wsel), .wdata(wdata), + .asel(opsela), .adata(adata), + .bsel(opselb), .bdata(bdata) + ); + +mux2 #(32) wdata_mux( + .sel(ctl_ram_rd), + .in0(result), + .in1(d_data_r), + .out(wdata) + ); + +assign next_pc = pc + pc_adjust; + +wire S; +assign S = opimm16[15]; + +mux2 #(32) pc_source( + .sel(ctl_branch), + .in0(4), + .in1( {S,S,S,S,S,S,S,S,S,S,S,S,S,S,opimm16,2'h0} ), + .out(pc_adjust) + ); + +assign i_addr = pc; +assign ir = i_data; + +wire [31:0] binput; + +mux2 #(32) alu_right_mux( + .sel(ctl_imm16), + .in0(bdata), + .in1({ 16'h0, opimm16 }), + .out(binput) + ); + +mux2 #(4) alu_wsel_mux( + .sel(ctl_d_or_b), + .in0(opseld), + .in1(opselb), + .out(alu_wsel) + ); + +alu alu( + .opcode(ctl_alu_func), + .left(adata), + .right(binput), + .out(result) + ); + +// SW operation always writes Rb (aka Rd) +assign d_addr = result; +assign d_data_w = bdata; +assign d_we = ctl_ram_we; + +endmodule diff --git a/isa.txt b/isa.txt @@ -0,0 +1,66 @@ +CPU32 Instruction Set Architecture +---------------------------------- + +Encoding Formats +---------------- + +R OOOOFFFFAAAABBBBDDDDXXXXXXXXXXXX register +I OOOOFFFFAAAADDDDIIIIIIIIIIIIIIII immediate +L OOOOFFFFIIIIIIIIIIIIIIIIIIIIIIII large (not currently used) + +Core Instruction Set +-------------------- + +0X ALU Rd, Ra, Rb see ALU ops below +1X ALU Rd, Ra, #I + +20 LW Rd, [Ra, #I] Rd = M(Ra + I) (TBD F->lane/width select) +30 SW Rd, [Ra, #I] M(Ra + I) = Rd + +41 BZ Ra, rel16 if Ra == 0: PC += I +42 BNZ Ra, rel16 if Ra != 0: PC += I +43 B rel16 PC += I +49 BLZ Ra, rel16 if Ra == 0: R15 = PC + 4, PC += I +4A BLNZ Ra, rel16 if Ra != 0: R15 = PC + 4, PC += I +4B BL rel16 R15 = PC + 4, PC += I + +Extended Instruction Set +------------------------ + +51 BZ Ra, [Rb] if Ra == 0: PC = Rb +52 BNZ Ra, [Rb] if Ra != 0: PC = Rb +53 B [Rb] PC = Rb +59 BNZ Ra, [Rb] if Ra == 0: R15 = PC, PC = Rb +5A BLNZ Ra, [Rb] if Ra != 0: R15 = PC, PC = Rb +5B BL [Rb] R15 = PC, PC = Rb + +60 J rel24 PC = PC + I +68 JL rel24 R15 = PC + 4, PC = PC + I + +ALU Instructions (replace Rb w/ #I for immediate form) +------------------------------------------------------ + +X0 OR Rd, Ra, Rb Rd = Ra | Rb +X1 AND Rd, Ra, Rb Rd = Ra & Rb +X2 ADD Rd, Ra, Rb Rd = Ra + Rb +X3 SUB Rd, Ra, Rb Rd = Ra - Rb +X4 SHL Rd, Ra, Rb Rd = Ra << Rb[0:4] +X5 SHR Rd, Ra, Rb Rd = Ra >> Rb[0:4] +X6 XOR Rd, Ra, Rb Rd = Ra ^ Rb +X7 TBS Rd, Ra, Rb Rd = Ra & (1 << Rb) ? 1 : 0 + +X8 SEQ Rd, Ra, Rb Rd = Ra == Rb +X9 SLT Rd, Ra, Rb Rd = Ra < Rb +XA SGT Rd, Ra, Rb Rd = Ra > Rb +XB REV Rd, Ra Rd = reversebits(Ra) +XC BIS Rd, Ra, Rb Rd = Ra | (1 << Rb) +XD BIC Rd, Ra, Rb Rd = Ra & ~(1 << Rb) +XE MOV Rd, Rb Rd = Rb +XF MHI Rd, Ra, Rb Rd = (Ra & 0xFFFF) | (Rb << 16) + +Open Issues +----------- +- should ALU IMM16 be signed? If so, how do we load unsigned data? +- consider dropping REV in favor of MOVU or MOVS? +- figure out func field coding for variant LW/SW instructions + diff --git a/library.v b/library.v @@ -0,0 +1,145 @@ +// Useful tidbits +// +// Copyright 2009, Brian Swetland. Use at your own risk. + +module decoder2 ( + input [1:0] in, + output out0, out1, out2, out3 + ); + +reg [3:0] out; + +assign out0 = out[0]; +assign out1 = out[1]; +assign out2 = out[2]; +assign out3 = out[3]; + +always @ (*) + case (in) + 2'b00: out = 4'b0001; + 2'b01: out = 4'b0010; + 2'b10: out = 4'b0100; + 2'b11: out = 4'b1000; + endcase + +endmodule + + +module decoder8 (input [2:0] in, output reg [7:0] out); +always @ (*) + case (in) + 3'b000: out = 8'b00000001; + 3'b001: out = 8'b00000010; + 3'b010: out = 8'b00000100; + 3'b011: out = 8'b00001000; + 3'b100: out = 8'b00010000; + 3'b101: out = 8'b00100000; + 3'b110: out = 8'b01000000; + 3'b111: out = 8'b10000000; + endcase +endmodule + +module decoder8en (input [2:0] in, input en, output reg [7:0] out); +always @ (*) + if (en) + case (in) + 3'b000: out = 8'b00000001; + 3'b001: out = 8'b00000010; + 3'b010: out = 8'b00000100; + 3'b011: out = 8'b00001000; + 3'b100: out = 8'b00010000; + 3'b101: out = 8'b00100000; + 3'b110: out = 8'b01000000; + 3'b111: out = 8'b10000000; + endcase + else + out = 8'b00000000; +endmodule + + +module mux2 #(parameter WIDTH=16) + ( + input sel, + input [WIDTH-1:0] in0, in1, + output [WIDTH-1:0] out + ); + +assign out = sel ? in1 : in0 ; + +endmodule + +module mux4 #(parameter WIDTH=16) + ( + input [1:0] sel, + input [WIDTH-1:0] in0,in1,in2,in3, + output reg [WIDTH-1:0] out + ); +always @ (*) + case (sel) + 2'b00: out <= in0; + 2'b01: out <= in1; + 2'b10: out <= in2; + 2'b11: out <= in3; + endcase +endmodule + + +module mux8 #(parameter WIDTH=16) ( + input [2:0] sel, + input [WIDTH-1:0] in0,in1,in2,in3,in4,in5,in6,in7, + output reg [WIDTH-1:0] out + ); +always @ (*) + case (sel) + 3'b000: out <= in0; + 3'b001: out <= in1; + 3'b010: out <= in2; + 3'b011: out <= in3; + 3'b100: out <= in4; + 3'b101: out <= in5; + 3'b110: out <= in6; + 3'b111: out <= in7; + endcase +endmodule + +module mux16 #(parameter WIDTH=16) ( + input [3:0] sel, + input [WIDTH-1:0] in00,in01,in02,in03,in04,in05,in06,in07, + input [WIDTH-1:0] in08,in09,in10,in11,in12,in13,in14,in15, + output reg [WIDTH-1:0] out + ); +always @ (*) + case (sel) + 4'b0000: out <= in00; + 4'b0001: out <= in01; + 4'b0010: out <= in02; + 4'b0011: out <= in03; + 4'b0100: out <= in04; + 4'b0101: out <= in05; + 4'b0110: out <= in06; + 4'b0111: out <= in07; + 4'b1000: out <= in08; + 4'b1001: out <= in09; + 4'b1010: out <= in10; + 4'b1011: out <= in11; + 4'b1100: out <= in12; + 4'b1101: out <= in13; + 4'b1110: out <= in14; + 4'b1111: out <= in15; + endcase +endmodule +module register #(parameter WIDTH=16) ( + input clk, + input en, + input [WIDTH-1:0] din, + output [WIDTH-1:0] dout + ); + +reg [WIDTH-1:0] data; +initial data = 0; +always @ (posedge clk) + if (en) + data <= din; +assign dout = data; +endmodule + diff --git a/ram.v b/ram.v @@ -0,0 +1,20 @@ +// RAM - Does not instantiate optimally on Altera FPGAs +// +// Copyright 2009, Brian Swetland. Use at your own risk. + +module ram #(parameter DWIDTH=16, parameter AWIDTH=3) ( + input clk, input we, + input [AWIDTH-1:0] addr, + input [DWIDTH-1:0] wdata, + output [DWIDTH-1:0] rdata + ); + +reg [DWIDTH-1:0] R[0:2**AWIDTH-1]; + +always @ (posedge clk) + if (we) + R[addr] <= wdata; + +assign rdata = R[addr]; + +endmodule diff --git a/regfile.v b/regfile.v @@ -0,0 +1,32 @@ +// Dual-reader / Single-writer Register File +// +// Copyright 2009, Brian Swetland. Use at your own risk. + +module regfile #(parameter DWIDTH=16, parameter AWIDTH=3) ( + input clk, input we, + input [AWIDTH-1:0] wsel, input [DWIDTH-1:0] wdata, + input [AWIDTH-1:0] asel, output [DWIDTH-1:0] adata, + input [AWIDTH-1:0] bsel, output [DWIDTH-1:0] bdata + ); + +reg [DWIDTH-1:0] R[0:2**AWIDTH-1]; + +initial begin + R[0] <= 0; + R[1] <= 0; + R[2] <= 0; + R[3] <= 0; + R[4] <= 0; + R[5] <= 0; + R[6] <= 0; + R[7] <= 0; + end + +always @ (posedge clk) + if (we) + R[wsel] <= wdata; + +assign adata = R[asel]; +assign bdata = R[bsel]; + +endmodule diff --git a/rom.asm b/rom.asm @@ -0,0 +1,20 @@ +NOP +MOV R0, 0x5038 +MHI R14, 0x7777 +MOV R14, 0 +SW R0, [R14, 0x10] +MOV R15, 0x2222 +NOP + +MOV R15, 0xFFD20000 +MOV R0, 0 +bigloop: +MOV R1, 65536 +loop: +SUB R1, R1, 1 +BNZ R1, loop +SW R0, [R15] +ADD R0, R0, 1 +B bigloop + + diff --git a/rom.v b/rom.v @@ -0,0 +1,22 @@ +// ROM +// +// Copyright 2009, Brian Swetland. Use at your own risk. + +`timescale 1ns/1ns + +module rom #(parameter DWIDTH=16, parameter AWIDTH=8) ( + input clk, + input [AWIDTH-1:0] addr, + output [DWIDTH-1:0] data + ); + + reg [DWIDTH-1:0] rom[0:2**AWIDTH-1]; + + initial + $readmemh("rom.txt", rom); + + assign data = rom[addr]; +endmodule + + + diff --git a/testbench.v b/testbench.v @@ -0,0 +1,68 @@ +// CPU32 Test Bench - For testing in iverilog. +// +// Copyright 2012, Brian Swetland. Use at your own risk. + +`timescale 1ns/1ns + +module testbench; + +reg clk, reset; +wire [31:0] romaddr, romdata, ramaddr, ramrdata, ramwdata; +wire ramwe; + +initial + begin + reset = 0; + #20 + reset = 1; + end + +always + begin + clk = 0; + #10 ; + clk = 1; + #10 ; + end + +cpu32 cpu( + .clk(clk), + .i_addr(romaddr), + .i_data(romdata), + .d_data_r(ramrdata), + .d_data_w(ramwdata), + .d_addr(ramaddr), + .d_we(ramwe) + ); + +rom #(32,8) rom( + .addr(romaddr[9:2]), + .data(romdata) + ); + +ram #(32,8) ram( + .clk(clk), + .addr(ramaddr[9:2]), + .rdata(ramrdata), + .wdata(ramwdata), + .we(ramwe) + ); + +initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0,testbench); +end + +initial #400 $finish; + +initial + $monitor("%05t: pc=%h ir=%h R> %h %h %h %h", + $time, cpu.pc, cpu.ir, + cpu.REGS.R[0], + cpu.REGS.R[1], + cpu.REGS.R[2], + cpu.REGS.R[3] + ); + +endmodule +