diff --git a/.gitignore b/.gitignore index b5d69ad9..424c5825 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,10 @@ nexys4ddr/webtalk_impact.xml nexys4ddr/iseconfig impact.xsl impact_impact.xwbt -qasm2rom +tools/qasm2rom register_file.sym -qnice -qasm +emulator/qnice +tools/qasm *.lis *.out *.rom diff --git a/emulator/qnice.c b/emulator/qnice.c index 1e27947e..a55b1446 100644 --- a/emulator/qnice.c +++ b/emulator/qnice.c @@ -73,7 +73,7 @@ typedef struct statistic_data int gbl$memory[MEMORY_SIZE], gbl$registers[REGMEM_SIZE], gbl$debug = FALSE, gbl$verbose = FALSE, gbl$normal_operands[] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, gbl$gather_statistics = FALSE, gbl$enable_uart = TRUE, - gbl$ctrl_c = FALSE; + gbl$ctrl_c = FALSE, gbl$breakpoint = -1; char *gbl$normal_mnemonics[] = {"MOVE", "ADD", "ADDC", "SUB", "SUBC", "SHL", "SHR", "SWAP", "NOT", "AND", "OR", "XOR", "CMP", "", "HALT"}, *gbl$branch_mnemonics[] = {"ABRA", "ASUB", "RBRA", "RSUB"}, @@ -550,28 +550,28 @@ int execute() source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 + source_1; update_status_bits(destination, source_0, source_1, MODIFY_ALL); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 2: /* ADDC */ source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 + source_1 + ((read_register(14) >> 2) & 1); /* Take carry into account */ update_status_bits(destination, source_0, source_1, MODIFY_ALL); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 3: /* SUB */ source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 - source_1; update_status_bits(destination, source_0, source_1, MODIFY_ALL); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 4: /* SUBC */ source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 - source_1 - ((read_register(14) >> 2) & 1); /* Take carry into account */ update_status_bits(destination, source_0, source_1, MODIFY_ALL); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 5: /* SHL */ source_0 = read_source_operand(source_mode, source_regaddr, FALSE); @@ -612,21 +612,21 @@ int execute() source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 & source_1; update_status_bits(destination, source_0, source_1, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 10: /* OR */ source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 | source_1; update_status_bits(destination, source_0, source_1, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 11: /* XOR */ source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); source_1 = read_source_operand(source_mode, source_regaddr, FALSE); destination = source_0 ^ source_1; update_status_bits(destination, source_0, source_1, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); - write_destination(destination_mode, destination_regaddr, destination, FALSE); + write_destination(destination_mode, destination_regaddr, destination, TRUE); break; case 12: /* CMP */ source_0 = read_source_operand(destination_mode, destination_regaddr, FALSE); @@ -682,6 +682,13 @@ int execute() return TRUE; } + if (read_register(15) == gbl$breakpoint) + { + printf("Breakpoint reached: %04X\n", address); + return TRUE; + } + + /* write_register(15, read_register(15) + 1); */ /* Update program counter */ return FALSE; /* No HALT instruction executed */ } @@ -858,6 +865,10 @@ int main(int argc, char **argv) { if (!strcmp(token, "QUIT") || !strcmp(token, "EXIT")) return 0; + else if (!strcmp(token, "CB")) + gbl$breakpoint = -1; + else if (!strcmp(token, "SB")) + printf("Breakpoint set to %04X\n", gbl$breakpoint = str2int(tokenize(NULL, delimiters))); else if (!strcmp(token, "DUMP")) { start = str2int(tokenize(NULL, delimiters)); @@ -949,6 +960,7 @@ int main(int argc, char **argv) } else if (!strcmp(token, "HELP")) printf("\n\ +CB Clear Breakpoint\n\ DEBUG Toggle debug mode (for development only)\n\ DIS , Disassemble a memory region\n\ DUMP , Dump a memory area, START and STOP can be\n\ @@ -958,8 +970,9 @@ QUIT/EXIT Stop the emulator and return to the shell\n\ RESET Reset the whole machine\n\ RDUMP Print a register dump\n\ RUN [] Run a program beginning at ADDR\n\ -SET Either set a register of a memory cell\n\ +SET Either set a register or a memory cell\n\ SAVE Create a loadable binary file\n\ +SB Set breakpoint to an address\n\ STAT Displays some execution statistics\n\ STEP [] Executes a single instruction at address\n\ ADDR. If not address is specified the current\n\ diff --git a/monitor/sysdef.asm b/monitor/sysdef.asm index 019e8d47..757acc62 100644 --- a/monitor/sysdef.asm +++ b/monitor/sysdef.asm @@ -22,6 +22,10 @@ IO$BASE .EQU 0xFC00 IO$UART0_BASE .EQU 0xFC00 +IO$TIL_BASE .EQU 0xFF10 ; Address of TIL-display +IO$TIL_MASK .EQU 0xFF11 ; Mask register of TIL display + + ; ; UART-registers: ; diff --git a/test_programs/bram.asm b/test_programs/bram.asm index 9938beb4..3389d112 100644 --- a/test_programs/bram.asm +++ b/test_programs/bram.asm @@ -16,13 +16,17 @@ EXE_START .EQU 0x8011 ; start address of code in RAM MOVE CODE_START, R1 ; run variable for copying: source MOVE EXE_START, R2 ; run variable for copying: dest MOVE 1, R3 ; we need to subtract 1 often - SUB R1, R0 ; how many words to copy - 1 - ; as the last opcode 2 words + SUB R1, R0 ; how many words to copy + ; caution: if the last opcode + ; consists of two words, this + ; needs to be incremented or + ; the label needs to be put one + ; line below COPY_CODE MOVE @R1++, @R2++ ; copy from src to dst SUB R3, R0 ; one less item to go - RBRA COPY_CODE, !N ; R0 is #words-1, so check for !N - ; instead of checking for !Z + RBRA COPY_CODE, !N ; R0 is decremented one time too + ; often so check for !N instead !Z ABRA EXE_START, 1 ; execute code from RAM diff --git a/test_programs/brborder.asm b/test_programs/brborder.asm new file mode 100644 index 00000000..c20245fd --- /dev/null +++ b/test_programs/brborder.asm @@ -0,0 +1,77 @@ +; unit test to provoke some BRAM borderline cases +; done by sy2002 on August, 22nd 2015 +; +; the unit test executes correctly, if the TIL display is showing the +; following sequence of numbers having about 1 sec delay in between +; ABAB, CDCD, EFEF, ACDC, CCCC, ACDC, EFEF, CDCD, FFFF, .... (repeat) + + .ORG 0x0000 + +#include "../monitor/sysdef.asm" + +RAMEXE .EQU 0x8000 +VARIABLE1 .EQU 0x80E0 +STACK .EQU 0x80FF + + MOVE STACK, SP + MOVE IO$TIL_BASE, R12 + + ; copy part of the logic to RAM + MOVE TORAM_END, R8 + SUB TORAM_START, R8 + MOVE TORAM_START, R9 + MOVE RAMEXE, R10 + ASUB COPY_CODE, 1 + + ; create the first two numbers of the seq: ABAB and CDCD + MOVE VARIABLE1, R0 + MOVE 0xABAB, @R0 + MOVE @R0++, R1 + MOVE 0xCDCD, @R0 + MOVE @R0++, R2 + + ; create EFEF, ACDC, ACDC, EFEF, CDCD, FFFF + ; note that the "CCCC" is inserted in the display routine + ASUB RAMEXE, 1 + +CONT_IN_ROM MOVE R1, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R2, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R3, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R4, @R12 + ASUB WAIT_A_SEC, 1 + MOVE 0xCCCC, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R5, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R6, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R7, @R12 + ASUB WAIT_A_SEC, 1 + MOVE R11, @R12 + ASUB WAIT_A_SEC, 1 + ABRA CONT_IN_ROM, 1 + +TORAM_START MOVE 0xEFEF, @R0 + MOVE @R0++, R3 + MOVE 0xACDC, @R0 + MOVE @R0, R4 + + MOVE R4, R5 + MOVE @--R0, R6 + MOVE @--R0, R7 + + ; intentionally strange to trigger some bram async reset + MOVE @--R0, R11 + MOVE R0, R10 + MOVE R11, @R0++ + MOVE 0x5454, @R0 + MOVE @R0, @R0 + ADD @R10, @R0 + MOVE @R0, R11 + RET +TORAM_END .BLOCK 1 + +#include "debug_tools.asm" \ No newline at end of file diff --git a/test_programs/debug_tools.asm b/test_programs/debug_tools.asm new file mode 100644 index 00000000..d5b65073 --- /dev/null +++ b/test_programs/debug_tools.asm @@ -0,0 +1,37 @@ +; utility routines for debugging +; sysdef.asm needs to be included before this +; done by sy2002 in August 2015 + +; copy code from one memory destination (e.g. ROM) to another (e.g. RAM) +; R8: amount of instruction words to copy +; R9: src, R10: dst +; +; CAUTION FOR R8: if the last opcode consists of two words, then better +; place the label one line later, e.g. to a .BLOCK 1 statement, otherwise +; the second word of the last opcode will not be copied + +COPY_CODE MOVE @R9++, @R10++ ; copy from src to dst + SUB 1, R8 ; one less item to go + RBRA COPY_CODE, !N ; R0 is decremented one time too + ; much, so check for !N instead of + ; checking for !Z + RET ; return from sub routine + +; sub routine to wait for about 1 second (assuming a ~10 MIPS operation) + +WAIT_A_SEC MOVE 0x1388, R8 ; inner wait cycles: 5.000 decimal + MOVE 0x09C4, R9 ; outer wait cycles: 2.500 decimal + RSUB WAIT_A_WHILE, 1 ; wait + RET ; return from sub routine + +; sub routine to wait for R8 x R9 cycles + +WAIT_A_WHILE INCRB ; next register bank + MOVE R9, R1 ; outer wait cycles +WAS_WAIT_LOOP2 MOVE R8, R0 ; inner wait cycles +WAS_WAIT_LOOP1 SUB 1, R0 ; dec inner wait cycles and ... + RBRA WAS_WAIT_LOOP1, !Z ; ... repeat if not zero + SUB 1, R1 ; dec outer wait cycles and ... + RBRA WAS_WAIT_LOOP2, !Z ; ... repeat if not zero + DECRB ; restore previous register bank + RET ; return from sub routine diff --git a/tools/qasm.c b/tools/qasm.c index 97013c74..73ade998 100644 --- a/tools/qasm.c +++ b/tools/qasm.c @@ -548,8 +548,8 @@ int decode_operand(char *operand, int *op_code) */ int assemble() { - int opcode, type, line_counter, address, i, error_counter = 0, number_of_operands, negate, flag, value, size, - special_char; + int opcode, type, line_counter, address = 0, i, error_counter = 0, number_of_operands, negate, flag, value, size, + special_char, org_found = 0; char line[STRING_LENGTH], *p, *delimiters = " ,", *token, *sr_bits = "1XCZNVIM"; data_structure *entry; @@ -557,7 +557,7 @@ int assemble() #ifdef DEBUG printf("assemble: Starting first pass.\n"); #endif - for (address = line_counter = 1, entry = gbl$data; entry; entry = entry->next, line_counter++) + for (line_counter = 1, entry = gbl$data; entry; entry = entry->next, line_counter++) { strcpy(line, entry->source); /* Get a local copy of the line and clean it up */ entry->state = STATE$NOTHING_YET_DONE; /* Still a lot to do */ diff --git a/vhdl/env1_globals.vhd b/vhdl/env1_globals.vhd index 184ebbbd..705470c4 100644 --- a/vhdl/env1_globals.vhd +++ b/vhdl/env1_globals.vhd @@ -10,8 +10,8 @@ use IEEE.STD_LOGIC_1164.all; package env1_globals is -- file name and file size (in lines) of the file that is converted to the ROM located at 0x0000 -constant ROM_FILE : string := "../test_programs/bram.rom"; -constant ROM_SIZE : integer := 38; +constant ROM_FILE : string := "../test_programs/brborder.rom"; +constant ROM_SIZE : integer := 101; -- size of lower register bank: should be 256 -- set to 16 during development for faster synthesis, routing, etc. diff --git a/vhdl/qnice_cpu.vhd b/vhdl/qnice_cpu.vhd index 793ae69c..8b5f3f44 100644 --- a/vhdl/qnice_cpu.vhd +++ b/vhdl/qnice_cpu.vhd @@ -190,6 +190,12 @@ signal Alu_V : std_logic; begin +-- @TODO (priority 2): Completely re-do the Tristate buffer topic. The lenghty wait-state and the #0000 situation when +-- writing are not necessary! As we have enough flip-flops, the Tristate handling can be done in "real-time". +-- as this has probably some pretty far reaching impact, the question is when to implement this. On the other hand, +-- the longer we wait, the bigger the impact will be. Some analysis with some MOVE, ADD & Co indirect @memory read/write +-- scenario will for sure help and create a more clear view. + -- TriState buffer/driver for the 16 bit DATA bus DATA_driver : TriState_Buffer generic map @@ -335,23 +341,6 @@ begin fsmNextCpuState <= cs_std_seq; fsmInstruction <= (others => '0'); --- BTW: as soon as all this works, plus some more unit tests especially for some to be found complicated BRAM borderline --- cases (e.g. ADD @R1, @R2, everything in RAM plus before and after some instructions that challenge the asyncreset), --- I should checkin the ISA V1.2 docu PDF (create a qnice/doc folder) --- check in the current assembler and stuff (inside the qnice folder), the emulator (within qnice folder, and the other --- material from there) and call it "V1.0" (before nerd session), because this is then a fully fledged --- QNICE CPU working in the initival ENV1 scenario. V1.1 could then be: plus UART plus Bernd's monitor is running. --- Later, V2.0 might be the new ISA plus another scenario than env1, e.g. QBM-1. --- the tagging in GIT/GITHUB is important, so that in a later "retro" session, we can compare ISA 1.2 in env1 with --- a full "QBM-1" computer having the new ISA (e.g. new CMP command, etc.). --- Update all tools in qnice folder to vaxman's latest material from SourceForge. - --- @TODO (priority 2): Completely re-do the Tristate buffer topic. The lenghty wait-state and the #0000 situation when --- writing are not necessary! As we have enough flip-flops, the Tristate handling can be done in "real-time". --- as this has probably some pretty far reaching impact, the question is when to implement this. On the other hand, --- the longer we wait, the bigger the impact will be. Some analysis with some MOVE, ADD & Co indirect @memory read/write --- scenario will for sure help and create a more clear view. - -- as the previous state sets the direction control to read and the address to a meaningful value -- (i.e. 0 after cs_reset or current PC afterwards), the DATA_driver will take care, that at the -- falling edge of cs_fetch's clock cycle, DATA_From_Bus will contain the next opcode