/* cpusim.c * * Block-level simulator for a simple CPU. * 12-bit address bus, 16-bit data bus, 16-bit instructions (4-bit opcode, 12-bit constant field) * * Compile: cc -g -O2 -o cpusim cpusim.c * * Run: ./cpusim * ./cpusim -v * * Last edited: 2017-06-12 09:13:44 by piumarta on zora */ #include #include #include #define info if (opt_v) printf static int opt_v = 0; // non-zero if -v specified on the command-line typedef unsigned int bus; // generic bus of <= 32 bits width static bus pcD = 0; // the PC register static bus pcQ = 0; static bus romAddr = 0; // external ROM interface static bus romDataOut = 0; enum { ADDR= 0, LOAD, STORE, ADD, SUB, JUMP, JUMPZ }; // MS 3 bits of opcode enum { IMM= 0, MEM }; // LS bit of opcode #define INSN(OPC, SRC, OPD) ( ((OPC) << 13) + ((SRC) << 12) + (OPD) ) // program ROM contents static const bus rom[4096]= { INSN( ADDR, IMM, 2 ), // from memory location 2 ... INSN( LOAD, MEM, 0 ), // ... load the accumulator register INSN( ADD, IMM, 1 ), // add 1 to it INSN( STORE, IMM, 2 ), // store the accumulator register into location 2 INSN( JUMP, IMM, 0 ), // jump to program location 0 (infinite loop) }; static bus op = 0, bMuxSel = 0, constant = 0; // instruction decoder input and outputs static bus r = 0, jump = 0; static bus aluOp = 0, aluZ = 0; enum { ALU_OP_B = 0, ALU_OP_ADD, ALU_OP_SUB }; // values for aluOp enum { ALU_Z = 0, ALU_Z_1 }; static bus regD = 0; // the accumulator register static bus regQ = 0; static bus addrD = 0; // the address register static bus addrQ = 0; static bus rwD = 0; // the read/write register static bus rwQ = 0; static bus ramAddr = 0; // external RAM interface static bus ramDataIn = 0; static bus ramDataOut = 0; static bus ram[4096]; // external RAM contents static bus bMuxOut = 0; // ALU B input static bus aluOut = 0, aluFlagZ = 0; // ALU outputs static bus ldInc = 0; // AND gate between JUMP and aluZ wires // run the simulation for one clock cycle void simulate(void) { romAddr= pcQ; // ROM always connected to PC register output ramAddr= addrQ; // RAM always connected to ADDR register output // instruction fetch romDataOut= rom[romAddr & 0x0FFF]; info("addr %04x insn %04x\n", romAddr, romDataOut); // instruction decode op = (romDataOut >> 13) & 0x7; bMuxSel = (romDataOut >> 12) & 0x1; constant = romDataOut & 0xFFF; info("op %x b %x const %03x\n", op, bMuxSel, constant); switch (op) { case ADDR: aluOp = ALU_OP_B; aluZ = ALU_Z; r = 0; rwD = 1; jump = 0; break; case LOAD: aluOp = ALU_OP_B; aluZ = ALU_Z; r = 1; rwD = 1; jump = 0; break; case STORE: aluOp = ALU_OP_B; aluZ = ALU_Z; r = 0; rwD = 0; jump = 0; break; case ADD: aluOp = ALU_OP_ADD; aluZ = ALU_Z; r = 1; rwD = 1; jump = 0; break; case SUB: aluOp = ALU_OP_SUB; aluZ = ALU_Z; r = 1; rwD = 1; jump = 0; break; case JUMP: aluOp = ALU_OP_B; aluZ = ALU_Z_1; r = 0; rwD = 1; jump = 1; break; case JUMPZ: aluOp = ALU_OP_B; aluZ = ALU_Z; r = 0; rwD = 1; jump = 1; break; default: fprintf(stderr, "illegal instruction\n"); exit(1); } info("aluOp %i aluZ %i r %i rwD %i jump %i\n", aluOp, aluZ, r, rwD, jump); // operand fetch info("ramAddr %04x\n", ramAddr); ramDataOut = ram[ramAddr & 0x0fff]; switch (bMuxSel) { case IMM: bMuxOut = constant; break; case MEM: bMuxOut = ramDataOut; break; default: abort(); } info("bMuxOut %03x\n", bMuxOut); // execute switch (aluOp) { case ALU_OP_B: aluOut = bMuxOut; break; case ALU_OP_ADD: aluOut = (regQ + bMuxOut) & 0xFFFF; break; case ALU_OP_SUB: aluOut = (regQ - bMuxOut) & 0xFFFF; break; default: abort(); } switch (aluZ) { case ALU_Z: aluFlagZ = (0 == aluOut); break; case ALU_Z_1: aluFlagZ = 1; break; default: abort(); } info("aluOut %04x aluFlagZ %i\n", aluOut, aluFlagZ); // result write back regD = aluOut; addrD = bMuxOut; ramDataIn = regQ; info("regD %04x ramDataIn %04x\n", regD, ramDataIn); if (0 == rwQ) { ram[addrQ] = regQ; printf("ram[%04x] = %04x\n", addrQ, regQ); } // update pc ldInc = jump & aluFlagZ; switch (ldInc) { case 0: pcD = pcQ + 1; break; case 1: pcD = bMuxOut; break; default: abort(); } info("ldInc %i pcD %04x\n", ldInc, pcD); // clock tick info("D pc %04x, addr %04x, reg %04x\n", pcD, addrD, regD); info("Q pc %04x, addr %04x, reg %04x\n", pcQ, addrQ, regQ); pcQ = pcD; // all register inputs copied to outputs at clock tick addrQ = addrD; rwQ = rwD; switch (r) { case 0: break; case 1: regQ = regD; break; // accumulator update enabled default: abort(); } info("Q pc %04x, addr %04x, reg %04x\n", pcQ, addrQ, regQ); } int main(int argc, char **argv, char **envp) { for (int argn= 1; argn < argc; ++argn) { if (!strcmp(argv[argn], "-v")) ++opt_v; else { fprintf(stderr, "%s: unknown option `%s'\n", argv[0], argv[argn]); exit(1); } } for (int cycle= 0; cycle < 1000000; ++cycle) { info("------------ %i\n", cycle); simulate(); info("\n"); } return 0; }