OpenCores
URL https://opencores.org/ocsvn/bitserial/bitserial/trunk

Subversion Repositories bitserial

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

/bitserial/trunk/LICENSE
0,0 → 1,19
Copyright (c) 2019 Richard James Howe
 
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
/bitserial/trunk/bit-sim.gif Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
bitserial/trunk/bit-sim.gif Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: bitserial/trunk/bit-state.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: bitserial/trunk/bit-state.png =================================================================== --- bitserial/trunk/bit-state.png (nonexistent) +++ bitserial/trunk/bit-state.png (revision 2)
bitserial/trunk/bit-state.png Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: bitserial/trunk/bit-wave.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: bitserial/trunk/bit-wave.png =================================================================== --- bitserial/trunk/bit-wave.png (nonexistent) +++ bitserial/trunk/bit-wave.png (revision 2)
bitserial/trunk/bit-wave.png Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: bitserial/trunk/bit.bin =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: bitserial/trunk/bit.bin =================================================================== --- bitserial/trunk/bit.bin (nonexistent) +++ bitserial/trunk/bit.bin (revision 2)
bitserial/trunk/bit.bin Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: bitserial/trunk/bit.c =================================================================== --- bitserial/trunk/bit.c (nonexistent) +++ bitserial/trunk/bit.c (revision 2) @@ -0,0 +1,260 @@ +/* Bit-Serial CPU simulator + * LICENSE: MIT + * AUTHOR: Richard James Howe + * EMAIL: howe.r.j.89@gmail.com + * GIT: https://github.com/howerj/bit-serial */ +#ifdef __unix__ +#include +#include +#include +#include +#define __USE_POSIX199309 +#define _POSIX_C_SOURCE 199309L +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSIZE (8192u) +#define ESCAPE (27) + +typedef uint16_t mw_t; /* machine word */ + +typedef struct { + mw_t pc, acc, flg, m[MSIZE]; + FILE *in, *out; + mw_t ch, leds, switches; + long done, sleep_ms, sleep_every; +#ifdef __unix__ + struct termios oldattr; /* ugly, but needed for Unix only */ +#endif +} bcpu_t; + +enum { fCy, fZ, fNg, fR, fHLT, }; + +#ifdef __unix__ /* unix junk... */ +extern int fileno(FILE *file); + +static int os_getch(bcpu_t *b) { + assert(b); + return fgetc(b->in); +} + +static void sleep_us(const unsigned long microseconds) { + struct timespec ts = { + .tv_sec = (microseconds / 1000000ul), + .tv_nsec = (microseconds % 1000000ul) * 1000ul, + }; + nanosleep(&ts, NULL); +} + +static void os_sleep_ms(bcpu_t *b, unsigned ms) { + assert(b); + sleep_us(ms * 1000ul); +} + +static int os_kbhit(bcpu_t *b) { + assert(b); + const int fd = STDIN_FILENO; + if (!isatty(fd)) + return 1; + int bytes = 0; + ioctl(fd, FIONREAD, &bytes); + return !!bytes; +} + +static int os_init(bcpu_t *b) { + assert(b); + const int fd = fileno(b->in); + if (!isatty(fd)) + return 0; + if (tcgetattr(fd, &b->oldattr) < 0) + return -1; + struct termios newattr = b->oldattr; + newattr.c_iflag &= ~(ICRNL); + newattr.c_lflag &= ~(ICANON | ECHO); + if (tcsetattr(fd, TCSANOW, &newattr) < 0) + return -2; + return 0; +} + +static int os_deinit(bcpu_t *b) { + assert(b); + if (!isatty(fileno(b->in))) + return 0; + return tcsetattr(fileno(b->in), TCSANOW, &b->oldattr) < 0 ? -1 : 0; +} +#else +#ifdef _WIN32 +#include +extern int getch(void); +extern int kbhit(void); +static int os_getch(bcpu_t *b) { assert(b); return b->in == stdin ? getch() : fgetc(b->in); } +static int os_kbhit(bcpu_t *b) { assert(b); Sleep(1); return kbhit(); } +static void os_sleep_ms(bcpu_t *b, unsigned ms) { assert(b); Sleep(ms); } +static int os_init(bcpu_t *b) { assert(b); return 0; } +static int os_deinit(bcpu_t *b) { assert(b); return 0; } +#else +static int os_kbhit(bcpu_t *b) { assert(b); return 1; } +static int os_getch(bcpu_t *b) { assert(b); return fgetc(b->in); } +static void os_sleep_ms(bcpu_t *b, unsigned ms) { assert(b); (void)ms; } +static int os_init(bcpu_t *b) { assert(b); return 0; } +static int os_deinit(bcpu_t *b) { assert(b); return 0; } +#endif +#endif /** __unix__ **/ + +static int wrap_getch(bcpu_t *b) { + assert(b); + const int ch = os_getch(b); + if ((ch == ESCAPE) || (ch < 0)) + b->done = 1; + return ch; +} + +static int wrap_putch(bcpu_t *b, const int ch) { + assert(b); + const int r = fputc(ch, b->out); + if (fflush(b->out) < 0) + return -1; + return r; +} + +static inline unsigned bits(unsigned b) { + unsigned r = 0; + do if (b & 1) r++; while (b >>= 1); + return r; +} + +static inline mw_t add(mw_t a, mw_t b, mw_t *carry) { + assert(carry); + const mw_t r = a + b; + *carry &= ~(1u << fCy); + if (r < a && r < b) + *carry |= (1u << fCy); + return r; +} + +static inline mw_t bload(bcpu_t *b, mw_t addr) { + assert(b); + if (!(0x4000ul & addr)) + return b->m[addr % MSIZE]; + switch (addr & 0x7) { + case 0: return b->switches; + case 1: return (!os_kbhit(b) << 8ul) | (b->ch & 0xFF); + } + return 0; +} + +static inline void bstore(bcpu_t *b, mw_t addr, mw_t val) { + assert(b); + if (!(0x4000ul & addr)) { + if (addr >= MSIZE) + return; + b->m[addr % MSIZE] = val; + return; + } + switch (addr & 0x7) { + case 0: b->leds = val; break; + case 1: + if (val & (1u << 13)) { + wrap_putch(b, val & 0xFFu); + fflush(b->out); + } + if (val & (1u << 10)) + b->ch = wrap_getch(b); + break; + case 2: /* TX control */ break; + case 3: /* RX control */ break; + case 4: /* UART control */ break; + } +} + +static int bcpu(bcpu_t *b) { + assert(b); + int r = 0; + mw_t * const m = b->m, pc = b->pc, acc = b->acc, flg = b->flg; + + for (unsigned count = 0; b->done == 0; count++) { + if ((b->sleep_every && (count % b->sleep_every) == 0) && b->sleep_ms > 0) + os_sleep_ms(b, b->sleep_ms); + + const mw_t instr = m[pc % MSIZE]; + const mw_t op1 = instr & 0x0FFF; + const mw_t cmd = (instr >> 12u) & 0xFu; + + if (flg & (1u << fHLT)) + goto halt; + if (flg & (1u << fR)) { + pc = 0; + acc = 0; + flg = 0; + continue; + } + + flg &= ~((1u << fZ) | (1u << fNg)); + flg |= ((!acc) << fZ); /* set zero flag */ + flg |= ((!!(acc & 0x8000)) << fNg); /* set negative flag */ + + const mw_t lop = (cmd & 0x8) ? op1 : bload(b, op1); + + pc++; + switch (cmd) { + case 0x0: acc |= lop; break; /* OR */ + case 0x1: acc &= lop; break; /* AND */ + case 0x2: acc ^= lop; break; /* XOR */ + case 0x3: acc = add(acc, lop, &flg); break; /* ADD */ + + case 0x4: acc <<= bits(lop); break; /* LSHIFT */ + case 0x5: acc >>= bits(lop); break; /* RSHIFT */ + case 0x6: acc = bload(b, lop); break; /* LOAD */ + case 0x7: bstore(b, lop, acc); break; /* STORE */ + + case 0x8: acc = bload(b, op1); break; /* LOAD-C */ + case 0x9: bstore(b, op1, acc); break; /* STORE-C */ + case 0xA: acc = op1; break; /* LITERAL */ + case 0xB: break; /* UNUSED */ + + case 0xC: pc = op1; break; /* JUMP */ + case 0xD: if (!acc) pc = op1; break; /* JUMPZ */ + case 0xE: if (op1 & 1) flg = acc; else pc = acc; break; /* SET */ + case 0xF: if (op1 & 1) acc = flg; else acc = pc - 1u; break; /* GET */ + default: r = -1; goto halt; + } + } +halt: + b->pc = pc; + b->acc = acc; + b->flg = flg; + return r; +} + +int main(int argc, char **argv) { + static bcpu_t b = { .flg = 1u << fZ, .sleep_ms = 5, .sleep_every = 64 * 1024 }; + if (argc != 2) + return 1; + b.in = stdin; + b.out = stdout; + FILE *in = fopen(argv[1], "rb"); + if (!in) + return 2; + for (size_t i = 0; i < MSIZE; i++) { + unsigned pc = 0; + if (fscanf(in, "%x", &pc) != 1) + break; + b.m[i] = pc; + } + if (os_init(&b) < 0) + return 3; + setbuf(stdin, NULL); + setbuf(stdout, NULL); + const int r = bcpu(&b) < 0 ? 4 : 0; + if (os_deinit(&b) < 0) + return 5; + return r; +} + Index: bitserial/trunk/bit.fth =================================================================== --- bitserial/trunk/bit.fth (nonexistent) +++ bitserial/trunk/bit.fth (revision 2) @@ -0,0 +1,845 @@ +\ +\ Cross Compiler and eForth interpreter for the bit-serial CPU available at: +\ +\ +\ +\ This implements a Direct Threaded Code virtual machine on which we can +\ build a Forth interpreter. +\ +\ References: +\ +\ - +\ - +\ - +\ - +\ - +\ - 8086 eForth 1.0 by Bill Muench and C. H. Ting, 1990 +\ +\ The cross compiler has been tested and works with gforth versions 0.7.0 and +\ 0.7.3. An already compiled image (called 'bit.hex') should be available if +\ you do not have gforth installed. +\ + +only forth also definitions hex + +wordlist constant meta.1 +wordlist constant target.1 +wordlist constant assembler.1 +wordlist constant target.only.1 + +: (order) ( u wid*n n -- wid*n u n ) + dup if + 1- swap >r recurse over r@ xor if + 1+ r> -rot exit then r> drop then ; +: -order ( wid -- ) get-order (order) nip set-order ; +: +order ( wid -- ) dup >r -order get-order r> swap 1+ set-order ; + +meta.1 +order also definitions + + 2 constant =cell +4000 constant size ( 16384 bytes, 8192 cells ) +2000 constant =end ( 8192 bytes, leaving 4096 for Dual Port Block RAM ) + 40 constant =stksz + 60 constant =buf +0008 constant =bksp +000A constant =lf +000D constant =cr +007F constant =del + +create tflash size cells here over erase allot + +variable tdp +variable tep +variable tlast +size =cell - tep ! +0 tlast ! + +: :m meta.1 +order also definitions : ; +: ;m postpone ; ; immediate +:m there tdp @ ;m +:m tc! tflash + c! ;m +:m tc@ tflash + c@ ;m +:m t! over ff and over tc! swap 8 rshift swap 1+ tc! ;m +:m t@ dup tc@ swap 1+ tc@ 8 lshift or ;m +:m talign there 1 and tdp +! ;m +:m tc, there tc! 1 tdp +! ;m +:m t, there t! 2 tdp +! ;m +:m $literal [char] " word count dup tc, 0 ?do count tc, loop drop talign ;m +:m tallot tdp +! ;m +:m thead + talign + there tlast @ t, tlast ! + parse-word dup tc, 0 ?do count tc, loop drop talign ;m +:m hex# ( u -- addr len ) 0 <# base @ >r hex =lf hold # # # # r> base ! #> ;m +:m save-hex ( -- ) + parse-word w/o create-file throw + there 0 do i t@ over >r hex# r> write-file throw =cell +loop + close-file throw ;m +:m save-target ( -- ) + parse-word w/o create-file throw >r + tflash there r@ write-file throw r> close-file ;m +:m .h base @ >r hex u. r> base ! ;m +:m .d base @ >r decimal u. r> base ! ;m +:m twords + cr tlast @ + begin + dup tflash + =cell + count 1f and type space t@ + ?dup 0= until ;m +:m .stat + 0 if + ." target: " target.1 +order words cr cr + ." target-only: " target.only.1 +order words cr cr + ." assembler: " assembler.1 +order words cr cr + ." meta: " meta.1 +order words cr cr + then + ." used> " there dup ." 0x" .h ." / " .d cr ;m +:m .end only forth also definitions decimal ;m +:m atlast tlast @ ;m +:m tvar get-current >r meta.1 set-current create r> set-current there , t, does> @ ;m +:m label: get-current >r meta.1 set-current create r> set-current there , does> @ ;m +:m tdown =cell negate and ;m +:m tnfa =cell + ;m ( pwd -- nfa : move to name field address) +:m tcfa tnfa dup c@ $1F and + =cell + tdown ;m ( pwd -- cfa ) +:m compile-only tlast @ tnfa t@ $20 or tlast @ tnfa t! ;m ( -- ) +:m immediate tlast @ tnfa t@ $40 or tlast @ tnfa t! ;m ( -- ) +:m t' ' >body @ ;m +:m call 2/ C000 or ;m +:m t2/ 2/ ;m + +: iOR 0000 or t, ; +: iAND 1000 or t, ; +: iXOR 2000 or t, ; +: iADD 3000 or t, ; +: iLSHIFT 4000 or t, ; +: iRSHIFT 5000 or t, ; +: iLOAD 2/ 6000 or t, ; +: iSTORE 2/ 7000 or t, ; + +: iLOAD-C 2/ 8000 or t, ; +: iSTORE-C 2/ 9000 or t, ; +: iLITERAL A000 or t, ; +: iUNUSED B000 or t, ; +: iJUMP C000 or t, ; +: iJUMPZ D000 or t, ; +: iSET E000 or t, ; +: iGET F000 or t, ; + + 1 constant flgCy + 2 constant flgZ + 4 constant flgNg + 8 constant flgR +10 constant flgHlt + +: flags? 1 iGET ; +: flags! 1 iSET ; +: halt! flgHlt iLITERAL flags! ; +: branch 2/ iJUMP ; +: ?branch 2/ iJUMPZ ; +: zero? flags? 2 iAND ; +:m postpone t' branch ;m + +assembler.1 +order also definitions +: begin there ; +: until ?branch ; +: again branch ; +: if there 0 ?branch ; +: mark there 0 branch ; +: then begin 2/ over t@ or swap t! ; +: else mark swap then ; +: while if swap ; +: repeat branch then ; +assembler.1 -order +meta.1 +order also definitions + +\ ---- ---- ---- ---- ---- image generation ---- ---- ---- ---- ---- ---- + +0 t, \ must be 0 ('0 iOR' works in either indirect or direct mode ) +1 t, \ must be 1 ('1 iADD' works in either indirect or direct mode ) +2 t, \ must be 2 ('2 iADD' works in either indirect or direct mode ) +label: entry +0 t, \ entry point to virtual machine + +FFFF tvar set \ all bits set, -1 + FF tvar low \ lowest bytes set + 0 tvar \ entry point of virtual machine program, set later on + 0 tvar pwd \ previous word pointer + + 0 tvar ip \ instruction pointer + 0 tvar w \ working pointer + 0 tvar t \ temporary register + 0 tvar tos \ top of stack + 0 tvar h \ dictionary pointer + 0 tvar {state} \ compiler state + 0 tvar {hld} \ hold space pointer + 0 tvar {base} \ input/output radix, default = 16 + 0 tvar {dpl} \ number of places after fraction + 0 tvar {in} \ position in query string + 0 tvar {handler} \ throw/catch handler + 0 tvar {last} \ last defined word + 0 tvar #tib \ terminal input buffer + + =end dup tvar {sp0} tvar {sp} \ grows downwards + =end =stksz 2* - dup tvar {rp0} tvar {rp} \ grows upwards + =end =stksz 2* - =buf - constant TERMBUF \ pad buffer space + +TERMBUF =buf + constant =tbufend + +: vcell 1 ( cell '1' should contain '1' ) ; +: -vcell set 2/ ; +: --sp {sp} iLOAD-C vcell iADD {sp} iSTORE-C ; +: ++sp {sp} iLOAD-C -vcell iADD {sp} iSTORE-C ; +: --rp {rp} iLOAD-C -vcell iADD {rp} iSTORE-C ; +: ++rp {rp} iLOAD-C vcell iADD {rp} iSTORE-C ; + +\ ---- ---- ---- ---- ---- Forth VM ---- ---- ---- ---- ---- ---- ---- ---- + +label: start + start call entry t! + {sp0} iLOAD-C {sp} iSTORE-C + {rp0} iLOAD-C {rp} iSTORE-C + iLOAD-C + ip iSTORE-C + \ -- fall-through -- +label: vm ( The Forth virtual machine ) + ip iLOAD-C + w iSTORE-C + ip iLOAD-C 1 iADD ip iSTORE-C + w iLOAD-C + 0 iSET \ jump to next token + +label: {nest} ( function call: accumulator must contain '0 iGET' prior to call ) + w iSTORE-C ( store '0 iGET' into working pointer ) + ++rp + ip iLOAD-C + {rp} iSTORE + w iLOAD-C + 2 iADD + ip iSTORE-C + vm branch + +label: {unnest} ( return from function call ) + {rp} iLOAD + t iSTORE-C + --rp + t iLOAD-C + ip iSTORE-C + vm branch + +:m nest 0 iGET {nest} branch ;m +:m unnest {unnest} branch ;m +:m =nest {nest} call ;m +:m =unnest {unnest} call ;m +:m =0iGET F000 ;m + +:m :ht ( "name" -- : forth only routine ) + get-current >r target.1 set-current create + r> set-current CAFEBABE talign there , + nest + does> @ branch ( really a call ) ;m + +:m :t ( "name" -- : forth only routine ) + >in @ thead >in ! + get-current >r target.1 set-current create + r> set-current CAFEBABE talign there , + nest + does> @ branch ( really a call ) ;m + +:m :to ( "name" -- : forth only, target only routine ) + >in @ thead >in ! + get-current >r target.only.1 set-current create r> set-current + there , + nest CAFEBABE + does> @ branch ;m + +:m ;t CAFEBABE <> if abort" unstructured" then talign unnest target.only.1 -order ; + +:m a: ( "name" -- : assembly only routine, no header ) + CAFED00D + target.1 +order also definitions + create talign there , + assembler.1 +order + does> @ branch ;m +:m (a); CAFED00D <> if abort" unstructured" then assembler.1 -order ;m +:m a; (a); vm branch ;m + +label: IncIp ip iLOAD-C 1 iADD ip iSTORE-C vm branch +label: decSp tos iSTORE-C --sp vm branch + +a: opPush ( pushes the next value in instruction stream on to the stack ) + ++sp + tos iLOAD-C + {sp} iSTORE + ip iLOAD-C + t iSTORE-C + t iLOAD + tos iSTORE-C + IncIp branch + (a); + +a: opJump ( jump to next value in instruction stream ) +label: Jump + ip iLOAD + ip iSTORE-C + a; + +a: opJumpZ + tos iLOAD-C + t iSTORE-C + {sp} iLOAD + tos iSTORE-C + --sp + t iLOAD-C + if + IncIp branch + then + Jump branch + (a); + +a: opNext + {rp} iLOAD + if + set 2/ iADD + {rp} iSTORE + Jump branch + then + --rp + IncIp branch + (a); + +:m lit opPush t, ;m +:m [char] char opPush t, ;m +:m char char opPush t, ;m +:m =push [ t' opPush ] literal call ;m +:m =jump [ t' opJump ] literal call ;m +:m =jumpz [ t' opJumpZ ] literal call ;m +:m begin talign there ;m +:m until talign opJumpZ 2/ t, ;m +:m again talign opJump 2/ t, ;m +:m if opJumpZ there 0 t, ;m +:m mark opJump there 0 t, ;m +:m then there 2/ swap t! ;m +:m else mark swap then ;m +:m while if ;m +:m repeat swap again then ;m +:m aft drop mark begin swap ;m +:m next talign opNext 2/ t, ;m + +a: bye halt! a; ( -- : bye bye! ) +a: exit unnest a; ( -- : exit from current function ) + +a: lls ( u shift -- u : shift left by number of bits set ) + {sp} iLOAD + tos 2/ iLSHIFT + decSp branch + (a); + +a: lrs ( u shift -- u : shift right by number of bits set ) + {sp} iLOAD + tos 2/ iRSHIFT + decSp branch + (a); + +a: and ( u u -- u : bit wise AND ) + {sp} iLOAD + tos 2/ iAND + decSp branch + (a); + +a: or ( u u -- u : bit wise OR ) + {sp} iLOAD + tos 2/ iOR + decSp branch + (a); + +a: xor ( u u -- u : bit wise XOR ) + {sp} iLOAD + tos 2/ iXOR + decSp branch + (a); + +a: + ( u u -- u : Plain old addition ) + {sp} iLOAD + tos 2/ iADD + decSp branch + (a); + +a: um+ ( u u -- u f : Add with carry ) + {sp} iLOAD + tos 2/ iADD + {sp} iSTORE + flags? + flgCy iAND + tos iSTORE-C + a; + +a: @ ( a -- u : load a memory address ) + tos iLOAD-C + 1 iRSHIFT + tos iSTORE-C + tos iLOAD + tos iSTORE-C + a; + +a: ! ( u a -- store a cell at a memory address ) + tos iLOAD-C + 1 iRSHIFT + t iSTORE-C + {sp} iLOAD + t iSTORE + --sp + {sp} iLOAD + decSp branch + (a); + +a: c@ ( b -- c ) + tos iLOAD-C + 1 iRSHIFT + t iSTORE-C + t iLOAD + t iSTORE-C + tos iLOAD-C + 1 iAND if + t iLOAD-C + low 2/ iRSHIFT + else + t iLOAD-C + low 2/ iAND + then + tos iSTORE-C + a; + +a: dup ( u -- u u : duplicate item on top of stack ) + ++sp + tos iLOAD-C + {sp} iSTORE + a; + +a: drop ( u -- : drop it like it's hot ) + {sp} iLOAD + decSp branch + (a); + +a: swap ( u1 u2 -- u2 u1 : swap top two stack items ) + {sp} iLOAD + t iSTORE-C + tos iLOAD-C + {sp} iSTORE + t iLOAD-C + tos iSTORE-C + a; + +a: over ( u1 u2 -- u1 u2 u1 : reach over top of stack and copy next on stack ) + {sp} iLOAD + t iSTORE-C + ++sp + tos iLOAD-C + {sp} iSTORE + t iLOAD-C + tos iSTORE-C + a; + +a: 1- ( u -- u : decrement top of stack by one ) + tos iLOAD-C + set 2/ iADD + tos iSTORE-C + a; + +a: >r ( u -- , R: -- u : move variable from data to return stack ) + ++rp + tos iLOAD-C + {rp} iSTORE + {sp} iLOAD + decSp branch + (a); + +:m for talign >r begin ;m +:m =>r [ t' >r ] literal call ;m +:m =next [ t' opNext ] literal call ;m + +a: r> ( -- u , R: u -- : move variable from return to data stack ) + {rp} iLOAD + t iSTORE-C + --rp + ++sp + tos iLOAD-C + {sp} iSTORE + t iLOAD-C + tos iSTORE-C + a; + +a: r@ ( -- u, R: u -- u : copy top of return stack to data stack ) + ++sp + tos iLOAD-C + {sp} iSTORE + {rp} iLOAD + tos iSTORE-C + a; + +a: rdrop ( --, R: u -- : drop top item on return stack ) + --rp + a; + +a: execute ( xt -- : execute an execution token! ) + tos iLOAD-C + t iSTORE-C + {sp} iLOAD + tos iSTORE-C + --sp + t iLOAD-C + 1 iRSHIFT + {nest} branch + (a); + +a: sp! ( ??? u -- ??? : set stack depth ) + tos iLOAD-C + {sp} iSTORE-C + {sp} iLOAD + tos iSTORE-C + a; + +a: rp! ( u -- , R: ??? --- ??? : set return stack depth ) + tos iLOAD-C + {rp} iSTORE-C + {sp} iLOAD + decSp branch + (a); + +a: sp@ ( -- u : get variable stack depth ) + {sp} iLOAD-C + t iSTORE-C + ++sp + tos iLOAD-C + {sp} iSTORE + t iLOAD-C + tos iSTORE-C + a; + +a: rp@ ( -- u : get return stack depth ) + ++sp + tos iLOAD-C + {sp} iSTORE + {rp} iLOAD-C + tos iSTORE-C + a; + +\ ---- ---- ---- ---- ---- no more direct assembly ---- ---- ---- ---- ---- + +assembler.1 -order + +:ht #0 0 lit ;t ( -- 0 : space saving measure, push 0 onto variable stack ) +:ht #1 1 lit ;t ( -- 1 : space saving measure, push 1 onto variable stack ) +:ht #-1 -1 lit ;t ( -- -1 : space saving measure, push -1 onto variable stack ) + +\ Add words written in assembly into dictionary, you will need an understanding +\ of wordlists to understand this. + +:to bye bye ;t +:to and and ;t +:to or or ;t +:to xor xor ;t +:to + + ;t +:to um+ um+ ;t +:to @ @ ;t +:to ! ! ;t +:to c@ c@ ;t +:to dup dup ;t +:to drop drop ;t +:to swap swap ;t +:to over over ;t +:to 1- 1- ;t +:to >r r> swap >r >r ;t compile-only +:to r> r> r> swap >r ;t compile-only +:to r@ r> r@ swap >r ;t compile-only +:to execute execute ;t + +:t 0= if #0 exit then #-1 ;t +:t invert #-1 xor ;t +:t 1+ #1 + ;t +:t emit ( ch -- : ) + begin 8002 lit @ 1000 lit and 0= until + FF lit and 2000 lit or 8002 lit ! ;t +:t key? ( -- ch -1 | 0 ) + 8002 lit @ 100 lit and if #0 exit then + 400 lit 8002 lit ! 8002 lit @ FF lit and #-1 ;t +:t here h lit @ ;t +:t base {base} lit ;t ( -- a : base variable controls input/output radix ) +:t dpl {dpl} lit ;t ( -- a : push address of 'dpl' onto the variable stack ) +:t hld {hld} lit ;t ( -- a : push address of 'hld' onto the variable stack ) +:t bl 20 lit ;t ( -- space : push a space onto the stack ) +:t >in {in} lit ;t ( -- b : push pointer to terminal input position ) +:t hex $10 lit base ! ;t ( -- : switch to hexadecimal input/output radix ) +:t source TERMBUF lit #tib lit @ ;t ( -- b u ) +:t last {last} lit @ ;t ( -- : last defined word ) +:t state {state} lit ;t ( -- a : compilation state variable ) +:t ] #-1 state ! ;t ( -- : turn compile mode on ) +:t [ #0 state ! ;t immediate ( -- : turn compile mode off ) +:t nip swap drop ;t ( u1 u2 -- u2 : remove next stack value ) +:t tuck swap over ;t ( u1 u2 -- u2 u1 u2 : save top stack value ) +:t ?dup dup if dup then ;t ( u -- u u | 0 : duplicate if not zero ) +:t rot >r swap r> swap ;t ( u1 u2 u3 -- u2 u3 u1 : rotate three numbers ) +:t 2drop drop drop ;t ( u u -- : drop two numbers ) +:t 2dup over over ;t ( u1 u2 -- u1 u2 u1 u2 : duplicate set of values ) +:t +! tuck @ + swap ! ;t ( n a -- : increment value at address by 'n' ) +:t = xor 0= ;t ( u u -- f : equality ) +:t <> = 0= ;t ( u u -- f : inequality ) +:t 0>= 8000 lit and 0= ;t ( n -- f : greater or equal to zero ) +:t 0< 0>= 0= ;t ( n -- f : less than zero ) +:t negate 1- invert ;t ( n -- n : negate [twos compliment] ) +:t - negate + ;t ( u u -- u : subtract ) +:t < - 0< ;t ( n n -- f : signed less than ) +:t > swap < ;t ( n n -- f : signed greater than ) +:t 0> #0 > ;t ( n -- f : greater than zero ) +:t 2* #1 lls ;t ( u -- u : multiply by two ) +:t 2/ #1 lrs ;t ( u -- u : divide by two ) +:t cell 2 lit ;t ( -- u : size of memory cell ) +:t cell+ cell + ;t ( a -- a : increment address to next cell ) +:t pick sp@ + 2* @ ;t ( ??? u -- ??? u u : ) +:t u< 2dup 0>= swap 0>= xor >r < r> xor ;t ( u u -- f : ) +:t aligned dup #1 and + ;t ( b -- u : align a pointer ) +:t align here aligned h lit ! ;t ( -- : align dictionary pointer ) +:t depth {sp0} lit @ sp@ - 1- ;t ( -- u : variable stack depth ) +:t c! ( c b -- : store character at address ) + dup dup >r #1 and if + @ 00FF lit and swap FF lit lls + else + @ FF00 lit and swap FF lit and + then or r> ! ;t +:t count dup 1+ swap c@ ;t ( b -- b c : advance string, get next char ) +:t allot aligned h lit +! ;t ( u -- : allocate space in dictionary ) +:t , align here ! cell allot ;t ( u -- : write a value into the dictionary ) +:t abs dup 0< if negate then ;t ( n -- u : absolute value of a number ) +:t mux dup >r and swap r> invert and or ;t ( u1 u2 f -- ) +:t max 2dup < mux ;t ( n n -- n : maximum of two numbers ) +:t min 2dup > mux ;t ( n n -- n : minimum of two numbers ) +:t +string #1 over min rot over + rot rot - ;t ( b u -- b u : increment str ) +:t catch ( xt -- exception# | 0 \ return addr on stack ) + sp@ >r ( xt ) \ save data stack pointer + {handler} lit @ >r ( xt ) \ and previous handler + rp@ {handler} lit ! ( xt ) \ set current handler + execute ( ) \ execute returns if no throw + r> {handler} lit ! ( ) \ restore previous handler + rdrop ( ) \ discard saved stack ptr + #0 ;t ( 0 ) \ normal completion +:t throw ( ??? exception# -- ??? exception# ) + ?dup if ( exc# ) \ 0 throw is no-op + {handler} lit @ rp! ( exc# ) \ restore prev return stack + r> {handler} lit ! ( exc# ) \ restore prev handler + r> swap >r ( saved-sp ) \ exc# on return stack + sp! drop r> ( exc# ) \ restore stack + then ;t +:t um* ( u u -- ud : double cell width multiply ) + #0 swap ( u1 0 u2 ) $F lit + for dup um+ >r >r dup um+ r> + r> + if >r over um+ r> + then + next rot drop ;t +:t um/mod ( ud u -- ur uq : unsigned double cell width divide/modulo ) + ?dup 0= if -A lit throw then + 2dup u< + if negate $F lit + for >r dup um+ >r >r dup um+ r> + dup + r> r@ swap >r um+ r> or + if >r drop 1+ r> else drop then r> + next + drop swap exit + then 2drop drop #-1 dup ;t +:t key begin key? until ;t ( c -- : get a character from UART ) +:t type begin dup while swap count emit swap 1- repeat 2drop ;t ( b u -- ) +:t cmove for aft >r dup c@ r@ c! 1+ r> 1+ then next 2drop ;t ( b1 b2 u -- ) +:t do$ r> r> 2* dup count + aligned 2/ >r swap >r ;t ( -- a : ) +:t ($) do$ ;t ( -- a : do string NB. ) +:t .$ do$ count type ;t ( -- : print string, next cells contain string ) +:m ." .$ $literal ;m +:m $" ($) $literal ;m +:t space bl emit ;t ( -- : print space ) +:t cr .$ 2 tc, =cr tc, =lf tc, ;t ( -- : print new line ) +:t ktap ( bot eot cur c -- bot eot cur ) + dup dup =cr lit <> >r =lf lit <> r> and if \ Not End of Line? + dup =bksp lit <> >r =del lit <> r> and if \ Not Delete Char? + bl ( tap -> ) dup emit over c! 1+ ( bot eot cur c -- bot eot cur ) + exit + then + >r over r@ < dup if + =bksp lit dup emit space emit + then + r> + + exit + then drop nip dup ;t +:t accept ( b u -- b u : read in a line of user input ) + over + over + begin + 2dup xor + while + key dup bl - $5F lit u< if ( tap -> ) dup emit over c! 1+ else ktap then + repeat drop over - ;t +:t query TERMBUF lit =buf lit accept #tib lit ! drop #0 >in ! ;t ( -- : get line) +:t ?depth depth > if -4 lit throw then ;t ( u -- : check stack depth ) +:t -trailing ( b u -- b u : remove trailing spaces ) + for + aft bl over r@ + c@ < + if r> 1+ exit then + then + next #0 ;t +:ht look ( b u c xt -- b u : skip until *xt* test succeeds ) + swap >r rot rot + begin + dup + while + over c@ r@ - r@ bl = 4 lit pick execute + if rdrop rot drop exit then + +string + repeat rdrop rot drop ;t +:ht no-match if 0> exit then 0= 0= ;t ( c1 c2 -- t ) +:ht match no-match invert ;t ( c1 c2 -- t ) +:t parse ( c -- b u ; ) + >r source drop >in @ + #tib lit @ >in @ - r@ + >r over r> swap >r >r + r@ t' no-match lit look 2dup + r> t' match lit look swap r> - >r - r> 1+ ( b u c -- b u delta ) + >in +! + r> bl = if -trailing then #0 max ;t +:t spaces begin dup 0> while space 1- repeat drop ;t ( +n -- ) +:t hold #-1 hld +! hld @ c! ;t ( c -- : save a character in hold space ) +:t #> 2drop hld @ =tbufend lit over - ;t ( u -- b u ) +:t # ( d -- d : add next character in number to hold space ) + 2 lit ?depth + #0 base @ + ( extract ->) dup >r um/mod r> swap >r um/mod r> rot ( ud ud -- ud u ) + ( digit -> ) 9 lit over < 7 lit and + [char] 0 + ( u -- c ) + hold ;t +:t #s begin # 2dup ( d0= -> ) or 0= until ;t ( d -- 0 ) +:t <# =tbufend lit hld ! ;t ( -- ) +:t sign 0< if [char] - hold then ;t ( n -- ) +:t u.r >r #0 <# #s #> r> over - spaces type ;t ( u +n -- : print u right justified by +n ) +:t u. #0 <# #s #> space type ;t ( u -- : print unsigned number ) +:t . dup >r abs #0 <# #s r> sign #> space type ;t ( n -- print number ) +:t >number ( ud b u -- ud b u : convert string to number ) + begin + 2dup >r >r drop c@ base @ ( get next character ) + ( digit? -> ) >r [char] 0 - 9 lit over < + if 7 lit - dup $A lit < or then dup r> u< ( c base -- u f ) + 0= if ( d char ) + drop ( d char -- d ) + r> r> ( restore string ) + exit ( ..exit ) + then ( d char ) + swap base @ um* drop rot base @ um* ( d+ -> ) >r swap >r um+ r> + r> + ( accumulate digit ) + r> r> ( restore string ) + +string dup 0= ( advance string and test for end ) + until ;t +:t number? ( a u -- d -1 | a u 0 : string to a number [easier to use] ) + #-1 dpl ! + base @ >r + over c@ [char] - = dup >r if +string then + over c@ [char] $ = if hex +string then + >r >r #0 dup r> r> + begin + >number dup + while over c@ [char] . xor + if rot drop rot r> 2drop #0 r> base ! exit then + 1- dpl ! 1+ dpl @ + repeat + 2drop r> if + ( dnegate -> ) invert >r invert #1 um+ r> + + then r> base ! #-1 ;t +:t compare ( a1 u1 a2 u2 -- n : string equality ) + rot + over - ?dup if >r 2drop r> nip exit then + for ( a1 a2 ) + aft + count rot count rot - ?dup + if rdrop nip nip exit then + then + next 2drop #0 ;t +:to .s depth for aft r@ pick . then next ;t ( -- : print variable stack ) +:t nfa cell+ ;t ( pwd -- nfa : move word pointer to name field ) +:t cfa nfa dup c@ $1F lit and + cell+ cell negate and ;t ( pwd -- cfa ) +:t (find) ( a wid -- PWD PWD 1|PWD PWD -1|0 a 0: find word in WID ) + swap >r dup + begin + dup + while + dup nfa count $9F lit ( $1F:word-length + $80:hidden ) and r@ count compare 0= + if ( found! ) + rdrop + dup ( immediate? -> ) nfa $40 lit swap @ and 0= 0= + #1 or negate exit + then + nip dup @ + repeat + 2drop #0 r> #0 ;t +:t find last (find) rot drop ;t ( "name" -- b ) +:t literal state @ if =push lit , , then ;t immediate ( u -- ) +:t compile, 2/ align C000 lit or , ;t ( xt -- ) +:t ?found if exit then space count type [char] ? emit cr -D lit throw ;t ( u f -- ) +:t interpret ( b -- ) + find ?dup if + state @ + if + 0> if cfa execute exit then \ <- immediate word are executed + cfa compile, exit \ <- compiling word are...compiled. + then + drop + dup nfa c@ 20 lit and if -E lit throw then ( <- ?compile ) + cfa execute exit \ <- if its not, execute it, then exit *interpreter* + then + \ not a word + dup >r count number? if rdrop \ it is a number! + dpl @ 0< if \ <- dpl will be -1 if it is a single cell number + drop \ drop high cell from 'number?' for single cell output + else \ <- dpl is not -1, it is a double cell number + state @ if swap then + postpone literal \ literal is executed twice if it's a double + then + postpone literal exit + then + r> #0 ?found \ Could vector ?found here, to handle arbitrary words + ;t +:t word parse here dup >r 2dup ! 1+ swap cmove r> ;t ( c -- b ) +:to words last begin dup nfa count 1f lit and space type @ ?dup 0= until ;t +:to see bl word find ?found + cr begin dup @ =unnest lit <> while dup @ u. cell+ repeat @ u. ;t +:to : align here last , {last} lit ! ( "name" -- : define a new word ) + bl word + dup c@ 0= if -A lit throw then + count + h lit ! align + =0iGET lit , =nest lit , ] BABE lit ;t +:to ; postpone [ BABE lit <> if -16 lit throw then =unnest lit , ;t immediate compile-only +:to begin align here ;t immediate compile-only +:to until =jumpz lit , 2/ , ;t immediate compile-only +:to again =jump lit , 2/ , ;t immediate compile-only +:to if =jumpz lit , here #0 , ;t immediate compile-only +:to then here 2/ swap ! ;t immediate compile-only +:to for =>r lit , here ;t immediate compile-only +:to next =next lit , 2/ , ;t immediate compile-only +:to ' bl word find ?found cfa literal ;t immediate +:t compile r> dup 2* @ , 1+ >r ;t compile-only ( -- : compile next compiled into dictionary ) +:to exit compile exit ;t immediate compile-only +:to ." compile .$ [char] " word count + h lit ! align ;t immediate compile-only +:to $" compile ($) [char] " word count + h lit ! align ;t immediate compile-only \ " +:to ( [char] ) parse 2drop ;t immediate ( "comment" -- discard until parenthesis ) +:to \ source drop @ >in ! ;t immediate ( "comment" -- discard until end of line ) +:to immediate last nfa @ $40 lit or last nfa ! ;t ( -- : turn previously defined word into an immediate one ) +:to dump begin over c@ u. +string ?dup 0= until drop ;t +:t eval begin bl word dup c@ while interpret #1 ?depth repeat drop ." ok" cr ;t ( "word" -- ) +:t ini hex postpone [ #0 >in ! #-1 dpl ! ;t ( -- ) +:t quit ( -- : interpreter loop [and more, does more than most QUITs] ) + there t2/ t! \ program entry point set here + ." eForth 3.2" cr + ini + begin + query t' eval lit catch + ( ?error -> ) ?dup if + space . [char] ? emit cr ini + then again ;t + +\ ---- ---- ---- ---- ---- implementation finished ---- ---- ---- ---- ---- + +there h t! +atlast {last} t! +save-hex bit.hex +save-target bit.bin +.stat +.end +.( DONE ) cr +bye + Index: bitserial/trunk/bit.hex =================================================================== --- bitserial/trunk/bit.hex (nonexistent) +++ bitserial/trunk/bit.hex (revision 2) @@ -0,0 +1,2401 @@ +0000 +0001 +0002 +C019 +FFFF +00FF +0947 +0000 +0000 +0000 +0000 +0000 +12C2 +0000 +0000 +0000 +0000 +0000 +0000 +1282 +0000 +2000 +2000 +1F80 +1F80 +8015 +9016 +8017 +9018 +8006 +9008 +8008 +9009 +8008 +3001 +9008 +8009 +E000 +9009 +8018 +3001 +9018 +8008 +7018 +8009 +3002 +9008 +C01F +6018 +900A +8018 +3004 +9018 +800A +9008 +C01F +8008 +3001 +9008 +C01F +900B +8016 +3001 +9016 +C01F +8016 +3004 +9016 +800B +7016 +8008 +900A +600A +900B +C038 +6008 +9008 +C01F +800B +900A +6016 +900B +8016 +3001 +9016 +800A +D058 +C038 +C04B +6018 +D05E +3004 +7018 +C04B +8018 +3004 +9018 +C038 +A010 +E001 +C01F +C030 +C01F +6016 +400B +C03C +6016 +500B +C03C +6016 +100B +C03C +6016 +000B +C03C +6016 +200B +C03C +6016 +300B +C03C +6016 +300B +7016 +F001 +1001 +900B +C01F +800B +5001 +900B +600B +900B +C01F +800B +5001 +900A +6016 +700A +8016 +3001 +9016 +6016 +C03C +800B +5001 +900A +600A +900A +800B +1001 +D09B +800A +5005 +C09D +800A +1005 +900B +C01F +8016 +3004 +9016 +800B +7016 +C01F +6016 +C03C +6016 +900A +800B +7016 +800A +900B +C01F +6016 +900A +8016 +3004 +9016 +800B +7016 +800A +900B +C01F +800B +3004 +900B +C01F +8018 +3001 +9018 +800B +7018 +6016 +C03C +6018 +900A +8018 +3004 +9018 +8016 +3004 +9016 +800B +7016 +800A +900B +C01F +8016 +3004 +9016 +800B +7016 +6018 +900B +C01F +8018 +3004 +9018 +C01F +800B +900A +6016 +900B +8016 +3001 +9016 +800A +5001 +C026 +800B +9016 +6016 +900B +C01F +800B +9018 +6016 +C03C +8016 +900A +8016 +3004 +9016 +800B +7016 +800A +900B +C01F +8016 +3004 +9016 +800B +7016 +8018 +900B +C01F +F000 +C026 +C041 +0000 +C030 +F000 +C026 +C041 +0001 +C030 +F000 +C026 +C041 +FFFF +C030 +0000 +6203 +6579 +F000 +C026 +C062 +C030 +0220 +6103 +646E +F000 +C026 +C06D +C030 +022E +6F02 +0072 +F000 +C026 +C070 +C030 +023C +7803 +726F +F000 +C026 +C073 +C030 +024A +2B01 +F000 +C026 +C076 +C030 +0258 +7503 +2B6D +F000 +C026 +C079 +C030 +0264 +4001 +F000 +C026 +C080 +C030 +0272 +2101 +F000 +C026 +C086 +C030 +027E +6302 +0040 +F000 +C026 +C090 +C030 +028A +6403 +7075 +F000 +C026 +C09F +C030 +0298 +6404 +6F72 +0070 +F000 +C026 +C0A5 +C030 +02A6 +7304 +6177 +0070 +F000 +C026 +C0A7 +C030 +02B6 +6F04 +6576 +0072 +F000 +C026 +C0AE +C030 +02C6 +3102 +002D +F000 +C026 +C0B8 +C030 +02D6 +3E22 +0072 +F000 +C026 +C0C3 +C0A7 +C0BC +C0BC +C030 +02E4 +7222 +003E +F000 +C026 +C0C3 +C0C3 +C0A7 +C0BC +C030 +02F8 +7222 +0040 +F000 +C026 +C0C3 +C0D0 +C0A7 +C0BC +C030 +030C +6507 +6578 +7563 +6574 +F000 +C026 +C0DC +C030 +0320 +3002 +003D +F000 +C026 +C04E +01A2 +C101 +C065 +C10B +C030 +0332 +6906 +766E +7265 +0074 +F000 +C026 +C10B +C073 +C030 +0348 +3102 +002B +F000 +C026 +C106 +C076 +C030 +035C +6504 +696D +0074 +F000 +C026 +C041 +8002 +C080 +C041 +1000 +C06D +C19C +C04E +01BC +C041 +00FF +C06D +C041 +2000 +C070 +C041 +8002 +C086 +C030 +036C +6B04 +7965 +003F +F000 +C026 +C041 +8002 +C080 +C041 +0100 +C06D +C04E +01DF +C101 +C065 +C041 +0400 +C041 +8002 +C086 +C041 +8002 +C080 +C041 +00FF +C06D +C10B +C030 +039E +6804 +7265 +0065 +F000 +C026 +C041 +0018 +C080 +C030 +03D8 +6204 +7361 +0065 +F000 +C026 +C041 +001E +C030 +03EC +6403 +6C70 +F000 +C026 +C041 +0020 +C030 +03FE +6803 +646C +F000 +C026 +C041 +001C +C030 +040E +6202 +006C +F000 +C026 +C041 +0020 +C030 +041E +3E03 +6E69 +F000 +C026 +C041 +0022 +C030 +042E +6803 +7865 +F000 +C026 +C041 +0010 +C1FA +C086 +C030 +043E +7306 +756F +6372 +0065 +F000 +C026 +C041 +1F20 +C041 +0028 +C080 +C030 +0452 +6C04 +7361 +0074 +F000 +C026 +C041 +0026 +C080 +C030 +046C +7305 +6174 +6574 +F000 +C026 +C041 +001A +C030 +0480 +5D01 +F000 +C026 +C10B +C244 +C086 +C030 +0492 +5B41 +F000 +C026 +C101 +C244 +C086 +C030 +04A2 +6E03 +7069 +F000 +C026 +C0A7 +C0A5 +C030 +04B2 +7404 +6375 +006B +F000 +C026 +C0A7 +C0AE +C030 +04C2 +3F04 +7564 +0070 +F000 +C026 +C09F +C04E +0274 +C09F +C030 +04D4 +7203 +746F +F000 +C026 +C0BC +C0A7 +C0C3 +C0A7 +C030 +04EA +3205 +7264 +706F +F000 +C026 +C0A5 +C0A5 +C030 +04FE +3204 +7564 +0070 +F000 +C026 +C0AE +C0AE +C030 +0510 +2B02 +0021 +F000 +C026 +C265 +C080 +C076 +C0A7 +C086 +C030 +0522 +3D01 +F000 +C026 +C073 +C19C +C030 +0538 +3C02 +003E +F000 +C026 +C29E +C19C +C030 +0546 +3003 +3D3E +F000 +C026 +C041 +8000 +C06D +C19C +C030 +0556 +3002 +003C +F000 +C026 +C2AE +C19C +C030 +056A +6E06 +6765 +7461 +0065 +F000 +C026 +C0B8 +C1A9 +C030 +057A +2D01 +F000 +C026 +C2C2 +C076 +C030 +058E +3C01 +F000 +C026 +C2C9 +C2B8 +C030 +059C +3E01 +F000 +C026 +C0A7 +C2D0 +C030 +05AA +3002 +003E +F000 +C026 +C101 +C2D7 +C030 +05B8 +3202 +002A +F000 +C026 +C106 +C067 +C030 +05C8 +3202 +002F +F000 +C026 +C106 +C06A +C030 +05D8 +6304 +6C65 +006C +F000 +C026 +C041 +0002 +C030 +05E8 +6305 +6C65 +2B6C +F000 +C026 +C2F8 +C076 +C030 +05FA +7004 +6369 +006B +F000 +C026 +C0EF +C076 +C2E7 +C080 +C030 +060C +7502 +003C +F000 +C026 +C28C +C2AE +C0A7 +C2AE +C073 +C0BC +C2D0 +C0C3 +C073 +C030 +0622 +6107 +696C +6E67 +6465 +F000 +C026 +C09F +C106 +C06D +C076 +C030 +0640 +6105 +696C +6E67 +F000 +C026 +C1F0 +C325 +C041 +0018 +C086 +C030 +0658 +6405 +7065 +6874 +F000 +C026 +C041 +002A +C080 +C0EF +C2C9 +C0B8 +C030 +0670 +6302 +0021 +F000 +C026 +C09F +C09F +C0BC +C106 +C06D +C04E +035B +C080 +C041 +00FF +C06D +C0A7 +C041 +00FF +C067 +C04B +0363 +C080 +C041 +FF00 +C06D +C0A7 +C041 +00FF +C06D +C070 +C0C3 +C086 +C030 +068A +6305 +756F +746E +F000 +C026 +C09F +C1B1 +C0A7 +C090 +C030 +06CE +6105 +6C6C +746F +F000 +C026 +C325 +C041 +0018 +C294 +C030 +06E4 +2C01 +F000 +C026 +C330 +C1F0 +C086 +C2F8 +C376 +C030 +06FA +6103 +7362 +F000 +C026 +C09F +C2B8 +C04E +0391 +C2C2 +C030 +070E +6D03 +7875 +F000 +C026 +C09F +C0BC +C06D +C0A7 +C0C3 +C1A9 +C06D +C070 +C030 +0724 +6D03 +7861 +F000 +C026 +C28C +C2D0 +C395 +C030 +0740 +6D03 +6E69 +F000 +C026 +C28C +C2D7 +C395 +C030 +0752 +2B07 +7473 +6972 +676E +F000 +C026 +C106 +C0AE +C3AC +C278 +C0AE +C076 +C278 +C278 +C2C9 +C030 +0764 +6305 +7461 +6863 +F000 +C026 +C0EF +C0BC +C041 +0024 +C080 +C0BC +C0F9 +C041 +0024 +C086 +C0DC +C0C3 +C041 +0024 +C086 +C0D8 +C101 +C030 +0786 +7405 +7268 +776F +F000 +C026 +C26E +C04E +03F2 +C041 +0024 +C080 +C0EB +C0C3 +C041 +0024 +C086 +C0C3 +C0A7 +C0BC +C0E6 +C0A5 +C0C3 +C030 +07B6 +7503 +2A6D +F000 +C026 +C101 +C0A7 +C041 +000F +C0BC +C09F +C079 +C0BC +C0BC +C09F +C079 +C0C3 +C076 +C0C3 +C04E +040D +C0BC +C0AE +C079 +C0C3 +C076 +C059 +03FD +C278 +C0A5 +C030 +07E6 +7506 +2F6D +6F6D +0064 +F000 +C026 +C26E +C19C +C04E +0420 +C041 +FFF6 +C3DF +C28C +C314 +C04E +0448 +C2C2 +C041 +000F +C0BC +C0BC +C09F +C079 +C0BC +C0BC +C09F +C079 +C0C3 +C076 +C09F +C0C3 +C0D0 +C0A7 +C0BC +C079 +C0C3 +C070 +C04E +0441 +C0BC +C0A5 +C1B1 +C0C3 +C04B +0442 +C0A5 +C0C3 +C059 +0428 +C0A5 +C0A7 +C065 +C283 +C0A5 +C10B +C09F +C030 +0824 +6B03 +7965 +F000 +C026 +C1D3 +C04E +0452 +C030 +089A +7404 +7079 +0065 +F000 +C026 +C09F +C04E +0466 +C0A7 +C36B +C1BA +C0A7 +C0B8 +C04B +045C +C283 +C030 +08AC +6305 +6F6D +6576 +F000 +C026 +C0BC +C04B +0479 +C0BC +C09F +C090 +C0D0 +C348 +C1B1 +C0C3 +C1B1 +C059 +0471 +C283 +C030 +08D0 +6403 +246F +F000 +C026 +C0C3 +C0C3 +C2E7 +C09F +C36B +C076 +C325 +C2EF +C0BC +C0A7 +C0BC +C030 +08FA +2803 +2924 +F000 +C026 +C480 +C030 +091C +2E02 +0024 +F000 +C026 +C480 +C36B +C45A +C030 +092A +7305 +6170 +6563 +F000 +C026 +C212 +C1BA +C030 +093C +6302 +0072 +F000 +C026 +C498 +0D02 +000A +C030 +094E +6B04 +6174 +0070 +F000 +C026 +C09F +C09F +C041 +000D +C2A6 +C0BC +C041 +000A +C2A6 +C0C3 +C06D +C04E +04E6 +C09F +C041 +0008 +C2A6 +C0BC +C041 +007F +C2A6 +C0C3 +C06D +C04E +04D6 +C212 +C09F +C1BA +C0AE +C348 +C1B1 +C065 +C0BC +C0AE +C0D0 +C2D0 +C09F +C04E +04E3 +C041 +0008 +C09F +C1BA +C4A2 +C1BA +C0C3 +C076 +C065 +C0A5 +C25C +C09F +C030 +0960 +6106 +6363 +7065 +0074 +F000 +C026 +C0AE +C076 +C0AE +C28C +C073 +C04E +050B +C450 +C09F +C212 +C2C9 +C041 +005F +C314 +C04E +0508 +C09F +C1BA +C0AE +C348 +C1B1 +C04B +0509 +C4B4 +C04B +04F4 +C0A5 +C0AE +C2C9 +C030 +09D4 +7105 +6575 +7972 +F000 +C026 +C041 +1F20 +C041 +0060 +C4EF +C041 +0028 +C086 +C0A5 +C101 +C21A +C086 +C030 +0A1E +3F06 +6564 +7470 +0068 +F000 +C026 +C33C +C2D7 +C04E +0530 +C041 +FFFC +C3DF +C030 +0A44 +2D09 +7274 +6961 +696C +676E +F000 +C026 +C0BC +C04B +0547 +C212 +C0AE +C0D0 +C076 +C090 +C2D0 +C04E +0547 +C0C3 +C1B1 +C065 +C059 +053C +C101 +C030 +F000 +C026 +C0A7 +C0BC +C278 +C278 +C09F +C04E +0568 +C0AE +C090 +C0D0 +C2C9 +C0D0 +C212 +C29E +C041 +0004 +C30A +C0DC +C04E +0565 +C0D8 +C278 +C0A5 +C065 +C3B7 +C04B +0551 +C0D8 +C278 +C0A5 +C030 +F000 +C026 +C04E +0572 +C2DF +C065 +C19C +C19C +C030 +F000 +C026 +C56C +C1A9 +C030 +0A62 +7005 +7261 +6573 +F000 +C026 +C0BC +C22E +C0A5 +C21A +C080 +C076 +C041 +0028 +C080 +C21A +C080 +C2C9 +C0D0 +C0BC +C0AE +C0C3 +C0A7 +C0BC +C0BC +C0D0 +C041 +0AD8 +C54B +C28C +C0C3 +C041 +0AEA +C54B +C0A7 +C0C3 +C2C9 +C0BC +C2C9 +C0C3 +C1B1 +C21A +C294 +C0C3 +C212 +C29E +C04E +05AB +C537 +C101 +C3A3 +C030 +0AF4 +7306 +6170 +6563 +0073 +F000 +C026 +C09F +C2DF +C04E +05BD +C4A2 +C0B8 +C04B +05B5 +C0A5 +C030 +0B5C +6804 +6C6F +0064 +F000 +C026 +C10B +C20A +C294 +C20A +C080 +C348 +C030 +0B7E +2302 +003E +F000 +C026 +C283 +C20A +C080 +C041 +1F80 +C0AE +C2C9 +C030 +0B98 +2301 +F000 +C026 +C041 +0002 +C527 +C101 +C1FA +C080 +C09F +C0BC +C417 +C0C3 +C0A7 +C0BC +C417 +C0C3 +C278 +C041 +0009 +C0AE +C2D0 +C041 +0007 +C06D +C076 +C041 +0030 +C076 +C5C3 +C030 +0BB2 +2302 +0073 +F000 +C026 +C5DB +C28C +C070 +C19C +C04E +05FE +C030 +0BF2 +3C02 +0023 +F000 +C026 +C041 +1F80 +C20A +C086 +C030 +0C0A +7304 +6769 +006E +F000 +C026 +C2B8 +C04E +061B +C041 +002D +C5C3 +C030 +0C1E +7503 +722E +F000 +C026 +C0BC +C101 +C608 +C5FC +C5CF +C0C3 +C0AE +C2C9 +C5B3 +C45A +C030 +0C38 +7502 +002E +F000 +C026 +C101 +C608 +C5FC +C5CF +C4A2 +C45A +C030 +0C58 +2E01 +F000 +C026 +C09F +C0BC +C38A +C101 +C608 +C5FC +C0C3 +C613 +C5CF +C4A2 +C45A +C030 +0C70 +3E07 +756E +626D +7265 +F000 +C026 +C28C +C0BC +C0BC +C0A5 +C090 +C1FA +C080 +C0BC +C041 +0030 +C2C9 +C041 +0009 +C0AE +C2D0 +C04E +0668 +C041 +0007 +C2C9 +C09F +C041 +000A +C2D0 +C070 +C09F +C0C3 +C314 +C19C +C04E +0672 +C0A5 +C0C3 +C0C3 +C065 +C0A7 +C1FA +C080 +C3F6 +C0A5 +C278 +C1FA +C080 +C3F6 +C0BC +C0A7 +C0BC +C079 +C0C3 +C076 +C0C3 +C076 +C0C3 +C0C3 +C3B7 +C09F +C19C +C04E +064F +C030 +0C90 +6E07 +6D75 +6562 +3F72 +F000 +C026 +C10B +C202 +C086 +C1FA +C080 +C0BC +C0AE +C090 +C041 +002D +C29E +C09F +C0BC +C04E +06A2 +C3B7 +C0AE +C090 +C041 +0024 +C29E +C04E +06AB +C222 +C3B7 +C0BC +C0BC +C101 +C09F +C0C3 +C0C3 +C64D +C09F +C04E +06CE +C0AE +C090 +C041 +002E +C073 +C04E +06C6 +C278 +C0A5 +C278 +C0C3 +C283 +C101 +C0C3 +C1FA +C086 +C065 +C0B8 +C202 +C086 +C1B1 +C202 +C080 +C04B +06B1 +C283 +C0C3 +C04E +06D9 +C1A9 +C0BC +C1A9 +C106 +C079 +C0C3 +C076 +C0C3 +C1FA +C086 +C10B +C030 +0D16 +6307 +6D6F +6170 +6572 +F000 +C026 +C278 +C0AE +C2C9 +C26E +C04E +06F0 +C0BC +C283 +C0C3 +C25C +C065 +C0BC +C04B +06FF +C36B +C278 +C36B +C278 +C2C9 +C26E +C04E +06FF +C0D8 +C25C +C25C +C065 +C059 +06F3 +C283 +C101 +C030 +0DBC +2E02 +0073 +F000 +C026 +C33C +C0BC +C04B +0710 +C0D0 +C30A +C63A +C059 +070D +C030 +0E08 +6E03 +6166 +F000 +C026 +C301 +C030 +0E26 +6303 +6166 +F000 +C026 +C716 +C09F +C090 +C041 +001F +C06D +C076 +C301 +C2F8 +C2C2 +C06D +C030 +0E34 +2806 +6966 +646E +0029 +F000 +C026 +C0A7 +C0BC +C09F +C09F +C04E +0757 +C09F +C716 +C36B +C041 +009F +C06D +C0D0 +C36B +C6E3 +C19C +C04E +0752 +C0D8 +C09F +C716 +C041 +0040 +C0A7 +C080 +C06D +C19C +C19C +C106 +C070 +C2C2 +C065 +C25C +C09F +C080 +C04B +0735 +C283 +C101 +C0C3 +C101 +C030 +0E56 +6604 +6E69 +0064 +F000 +C026 +C23A +C730 +C278 +C0A5 +C030 +0EB8 +6C47 +7469 +7265 +6C61 +F000 +C026 +C244 +C080 +C04E +0776 +C041 +C041 +C37F +C37F +C030 +0ECE +6308 +6D6F +6970 +656C +002C +F000 +C026 +C2EF +C330 +C041 +C000 +C070 +C37F +C030 +0EEE +3F06 +6F66 +6E75 +0064 +F000 +C026 +C04E +0790 +C065 +C4A2 +C36B +C45A +C041 +003F +C1BA +C4AA +C041 +FFF3 +C3DF +C030 +0F0C +6909 +746E +7265 +7270 +7465 +F000 +C026 +C760 +C26E +C04E +07C3 +C244 +C080 +C04E +07B4 +C2DF +C04E +07B1 +C71D +C0DC +C065 +C71D +C77D +C065 +C0A5 +C09F +C716 +C090 +C041 +0020 +C06D +C04E +07C0 +C041 +FFF2 +C3DF +C71D +C0DC +C065 +C09F +C0BC +C36B +C690 +C04E +07DA +C0D8 +C202 +C080 +C2B8 +C04E +07D2 +C0A5 +C04B +07D8 +C244 +C080 +C04E +07D7 +C0A7 +C76C +C76C +C065 +C0C3 +C101 +C78B +C030 +0F36 +7704 +726F +0064 +F000 +C026 +C57E +C1F0 +C09F +C0BC +C28C +C086 +C1B1 +C0A7 +C46C +C0C3 +C030 +0FBC +7705 +726F +7364 +F000 +C026 +C23A +C09F +C716 +C36B +C041 +001F +C06D +C4A2 +C45A +C080 +C26E +C19C +C04E +07F6 +C030 +0FDE +7303 +6565 +F000 +C026 +C212 +C7E2 +C760 +C78B +C4AA +C09F +C080 +C041 +C030 +C2A6 +C04E +081B +C09F +C080 +C62F +C301 +C04B +080E +C080 +C62F +C030 +1008 +3A01 +F000 +C026 +C330 +C1F0 +C23A +C37F +C041 +0026 +C086 +C212 +C7E2 +C09F +C090 +C19C +C04E +0833 +C041 +FFF6 +C3DF +C36B +C076 +C041 +0018 +C086 +C330 +C041 +F000 +C37F +C041 +C026 +C37F +C24B +C041 +BABE +C030 +103C +3B61 +F000 +C026 +C253 +C041 +BABE +C2A6 +C04E +0850 +C041 +FFEA +C3DF +C041 +C030 +C37F +C030 +1086 +6265 +6765 +6E69 +F000 +C026 +C330 +C1F0 +C030 +10A8 +7565 +746E +6C69 +F000 +C026 +C041 +C04E +C37F +C2EF +C37F +C030 +10BA +6165 +6167 +6E69 +F000 +C026 +C041 +C04B +C37F +C2EF +C37F +C030 +10D2 +6962 +0066 +F000 +C026 +C041 +C04E +C37F +C1F0 +C101 +C37F +C030 +10EA +7464 +6568 +006E +F000 +C026 +C1F0 +C2EF +C0A7 +C086 +C030 +1102 +6663 +726F +F000 +C026 +C041 +C0BC +C37F +C1F0 +C030 +1118 +6E64 +7865 +0074 +F000 +C026 +C041 +C059 +C37F +C2EF +C37F +C030 +112C +2741 +F000 +C026 +C212 +C7E2 +C760 +C78B +C71D +C76C +C030 +1144 +6327 +6D6F +6970 +656C +F000 +C026 +C0C3 +C09F +C2E7 +C080 +C37F +C1B1 +C0BC +C030 +115A +6564 +6978 +0074 +F000 +C026 +C8B2 +C065 +C030 +1178 +2E62 +0022 +F000 +C026 +C8B2 +C498 +C041 +0022 +C7E2 +C36B +C076 +C041 +0018 +C086 +C330 +C030 +118A +2462 +0022 +F000 +C026 +C8B2 +C491 +C041 +0022 +C7E2 +C36B +C076 +C041 +0018 +C086 +C330 +C030 +11AC +2841 +F000 +C026 +C041 +0029 +C57E +C283 +C030 +11CE +5C41 +F000 +C026 +C22E +C0A5 +C080 +C21A +C086 +C030 +11E0 +6909 +6D6D +6465 +6169 +6574 +F000 +C026 +C23A +C716 +C080 +C041 +0040 +C070 +C23A +C716 +C086 +C030 +11F4 +6404 +6D75 +0070 +F000 +C026 +C0AE +C090 +C62F +C3B7 +C26E +C19C +C04E +0912 +C0A5 +C030 +1218 +6504 +6176 +006C +F000 +C026 +C212 +C7E2 +C09F +C090 +C04E +092D +C7A1 +C106 +C527 +C04B +0922 +C0A5 +C498 +2003 +6B6F +C4AA +C030 +1238 +6903 +696E +F000 +C026 +C222 +C253 +C101 +C21A +C086 +C10B +C202 +C086 +C030 +1266 +7104 +6975 +0074 +F000 +C026 +C498 +650A +6F46 +7472 +2068 +2E33 +0032 +C4AA +C936 +C513 +C041 +1240 +C3C7 +C26E +C04E +095E +C4A2 +C63A +C041 +003F +C1BA +C4AA +C936 +C04B +0950 +C030 Index: bitserial/trunk/bit.vhd =================================================================== --- bitserial/trunk/bit.vhd (nonexistent) +++ bitserial/trunk/bit.vhd (revision 2) @@ -0,0 +1,516 @@ +-- File: bit.vhd +-- Author: Richard James Howe +-- Repository: https://github.com/howerj/bit-serial +-- License: MIT +-- Description: An N-bit, simple and small bit serial CPU + +library ieee, work, std; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; -- for debug only, not needed for synthesis + +-- The bit-serial CPU itself, the interface is bit-serial as well as the +-- CPU itself, address and data are N bits wide. The enable lines are held +-- high for N cycles when data is clocked in or out, and a complete read or +-- write consists of N cycles (although those cycles may not be contiguous, +-- it is up to the BCPU). The three enable lines are mutually exclusive, +-- only one will be active at any time. +-- +-- There are a few configurable items, but the defaults should work fine. +entity bcpu is + generic ( + asynchronous_reset: boolean := true; -- use asynchronous reset if true, synchronous if false + delay: time := 0 ns; -- simulation only, gate delay + N: positive := 16; -- size the CPU, minimum is 8 + parity: std_ulogic := '0'; -- set parity (even = '0'/odd = '1') of parity flag + jumpz: std_ulogic := '1'; -- jump on zero = '1', jump on non-zero = '0' + debug: natural := 0); -- debug level, 0 = off + port ( + clk, rst: in std_ulogic; + i: in std_ulogic; + o, a: out std_ulogic; + oe, ie, ae: buffer std_ulogic; + stop: out std_ulogic); +end; + +architecture rtl of bcpu is + type state_t is (RESET, FETCH, INDIRECT, OPERAND, EXECUTE, STORE, LOAD, ADVANCE, HALT); + type cmd_t is ( + iOR, iAND, iXOR, iADD, + iLSHIFT, iRSHIFT, iLOAD, iSTORE, + iLOADC, iSTOREC, iLITERAL, iUNUSED, + iJUMP, iJUMPZ, iSET, iGET); + + constant Cy: integer := 0; -- Carry; set by addition + constant Z: integer := 1; -- Accumulator is zero + constant Ng: integer := 2; -- Accumulator is negative + constant R: integer := 3; -- Reset CPU + constant HLT: integer := 4; -- Halt CPU + + type registers_t is record + state: state_t; -- state machine register + choice: state_t; -- computed next state + first: boolean; -- First flag, for setting up an instruction + last4: boolean; -- Are we processing the last 4 bits of the instruction? + indir: boolean; -- does the instruction require indirection of the operand? + tcarry: std_ulogic; -- temporary carry flag + dline: std_ulogic_vector(N - 1 downto 0); -- delay line, 16 cycles, our timer + acc: std_ulogic_vector(N - 1 downto 0); -- accumulator + pc: std_ulogic_vector(N - 1 downto 0); -- program counter + op: std_ulogic_vector(N - 1 downto 0); -- operand to instruction + flags: std_ulogic_vector(N - 1 downto 0); -- flags register + cmd: std_ulogic_vector(3 downto 0); -- instruction + end record; + + constant registers_default: registers_t := ( + state => RESET, + choice => RESET, + first => true, + last4 => false, + indir => false, + tcarry => 'X', + dline => (others => '0'), + acc => (others => 'X'), + pc => (others => 'X'), + op => (others => 'X'), + flags => (others => 'X'), + cmd => (others => 'X')); + + signal c, f: registers_t := registers_default; -- BCPU registers, all of them. + -- These signals are not used to hold state. The 'c' and 'f' registers + -- do that. + signal cmd: cmd_t; -- Shows up nicely in traces as an enumerated value + signal add1, add2, acin, ares, acout: std_ulogic; -- shared adder signals + signal last4, last: std_ulogic; -- state sequence signals + + -- 'adder' implements a full adder, which is all we need to implement + -- N-bit addition in a bit serial architecture. It is used in the instruction + -- and to increment the program counter. + procedure adder (x, y, cin: in std_ulogic; signal sum, cout: out std_ulogic) is + begin + sum <= x xor y xor cin after delay; + cout <= (x and y) or (cin and (x xor y)) after delay; + end procedure; + + -- 'bit_count' is used for assertions and nothing else. It counts the + -- number of bits in a 'std_ulogic_vector'. + function bit_count(bc: in std_ulogic_vector) return natural is + variable count: natural := 0; + begin + for index in bc'range loop + if bc(index) = '1' then + count := count + 1; + end if; + end loop; + return count; + end function; + + -- Obviously this does not synthesis, which is why synthesis is turned + -- off for the body of this function, it does make debugging much easier + -- though, we will be able to see which instructions are executed and do so + -- by name. + procedure print_debug_info is + variable ll: line; + + function hx(slv: in std_ulogic_vector) return string is -- std_ulogic_vector to hex string + constant cv: string := "0123456789ABCDEF"; + constant qu: integer := slv'length / 4; + constant rm: integer := slv'length mod 4; + variable rs: string(1 to qu); + variable sl: std_ulogic_vector(3 downto 0); + begin + assert rm = 0 severity failure; + for l in 0 to qu - 1 loop + sl := slv((l * 4) + 3 downto (l * 4)); + rs(qu - l) := cv(to_integer(unsigned(sl)) + 1); + end loop; + return rs; + end function; + + function yn(sl: std_ulogic; ch: character) return string is -- print a flag + variable rs: string(1 to 2) := "- "; + begin + if sl = '1' then + rs(1) := ch; + end if; + return rs; + end function; + begin + -- synthesis translate_off + if debug > 0 then + if c.state = EXECUTE and c.first then + write(ll, hx(c.pc) & ": "); + write(ll, cmd_t'image(cmd) & HT); + write(ll, hx(c.acc) & " "); + write(ll, hx(c.op) & " "); + write(ll, hx(c.flags) & " "); + write(ll, yn(c.flags(Cy), 'C')); + write(ll, yn(c.flags(Z), 'Z')); + write(ll, yn(c.flags(Ng), 'N')); + write(ll, yn(c.flags(R), 'R')); + write(ll, yn(c.flags(HLT), 'H')); + writeline(OUTPUT, ll); + end if; + if debug > 1 and last = '1' then + write(ll, state_t'image(c.state) & " => "); + write(ll, state_t'image(f.state)); + writeline(OUTPUT, ll); + end if; + end if; + -- synthesis translate_on + end procedure; +begin + assert N >= 8 report "CPU Width too small: N >= 8" severity failure; + assert not (ie = '1' and oe = '1') report "input/output at the same time" severity failure; + assert not (ie = '1' and ae = '1') report "input whilst changing address" severity failure; + assert not (oe = '1' and ae = '1') report "output whilst changing address" severity failure; + + adder (add1, add2, acin, ares, acout); -- shared adder + cmd <= cmd_t'val(to_integer(unsigned(c.cmd))); -- used for debug purposes + last4 <= c.dline(c.dline'high - 4) after delay; -- processing last four bits? + last <= c.dline(c.dline'high) after delay; -- processing last bit? + + process (clk, rst) begin + if rst = '1' and asynchronous_reset then + c.dline <= (others => '0') after delay; -- parallel reset! + c.state <= RESET after delay; + elsif rising_edge(clk) then + c <= f after delay; + if rst = '1' and not asynchronous_reset then + c.dline <= (others => '0') after delay; + c.state <= RESET after delay; + else + -- These are just assertions/debug logging, they are not required for + -- running, but we can make sure there are no unexpected state transitions, + -- and report on the internal state. + print_debug_info; + if c.state = RESET and last = '1' then assert f.state = FETCH; end if; + if c.state = LOAD and last = '1' then assert f.state = ADVANCE; end if; + if c.state = STORE and last = '1' then assert f.state = ADVANCE; end if; + if c.state = ADVANCE and last = '1' then assert f.state = FETCH; end if; + if c.state = HALT then assert f.state = HALT; end if; + if c.state = EXECUTE and last = '1' then + assert f.state = ADVANCE or f.state = LOAD or f.state = STORE or f.state = FETCH; + end if; + end if; + assert not (c.first xor f.dline(0) = '1') report "first/dline"; + end if; + end process; + + process (i, c, cmd, ares, acout, last, last4) begin + o <= '0' after delay; + a <= '0' after delay; + ie <= '0' after delay; + ae <= '0' after delay; + oe <= '0' after delay; + stop <= '0' after delay; + add1 <= '0' after delay; + add2 <= '0' after delay; + acin <= '0' after delay; + f <= c after delay; + f.dline <= c.dline(c.dline'high - 1 downto 0) & "0" after delay; + + -- Our delay line should only contain zero on one bit at a time + if c.first then + assert bit_count(c.dline) = 0 report "too many dline bits"; + else + assert bit_count(c.dline) = 1 report "missing dline bit"; + end if; + + -- The processor works by using a delay line (shift register) to + -- sequence actions, the top four bits are used for the + -- instruction (and if the highest bit is set indirection + -- is _not_ allowed), with the lowest twelve bits as an operand + -- to use as a literal value or an address. + -- + -- As such, we will want to trigger actions when processing the first + -- bit, the last four bits and the last bit. + -- + -- This delay line is used to save gates as opposed using a counter, + -- which would require an adder (but not a comparator - we could check + -- whether individual bits are set because all the comparisons are + -- against power of two values). + -- + if last = '1' then + f.state <= c.choice after delay; + f.first <= true after delay; + f.last4 <= false after delay; + -- This is a bit of a hack, in order to place it in its proper + -- place within the 'FETCH' state we would need to move the + -- 'indirection allowed on instruction' bit from the highest + -- bit to a lower bit so we can perform the state decision before + -- the bit is being processed. + if i = '0' and c.state = FETCH then + f.indir <= true; + f.state <= INDIRECT; -- Override FETCH Choice! + end if; + elsif last4 = '1' then + f.last4 <= true after delay; + end if; + + -- Each state lasts N (which defaults to 16) + 1 cycles. + -- Of note: we could make the FETCH state last only 4 + 1 cycles + -- and merge the operand fetching in FETCH (and OPERAND state) into + -- the 'EXECUTE' state. + case c.state is + when RESET => + f.choice <= FETCH; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + else + ae <= '1' after delay; + f.acc <= "0" & c.acc(c.acc'high downto 1) after delay; + f.pc <= "0" & c.pc (c.pc'high downto 1) after delay; + f.op <= "0" & c.op (c.op'high downto 1) after delay; + f.flags <= "0" & c.flags(c.flags'high downto 1) after delay; + end if; + -- When in the running state all state transitions pass through FETCH. + -- FETCH does what you expect from it, it fetches the instruction. It also + -- partially decodes it and sets flags that the accumulator depends on. + -- + -- What is meant by partially decoding is this; it is determined if we + -- should go to the INDIRECT state next or to the EXECUTE state, also + -- it is determined whether an I/O operation should be performed for those + -- instructions capable of doing I/O. + when FETCH => + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + f.indir <= false after delay; + f.flags(Z) <= '1' after delay; + else + ie <= '1' after delay; + + if c.acc(0) = '1' then -- determine flag status before EXECUTE + f.flags(Z) <= '0' after delay; + end if; + f.acc <= c.acc(0) & c.acc(c.acc'high downto 1) after delay; + + if not c.last4 then + f.op <= i & c.op(c.op'high downto 1) after delay; + else + f.cmd <= i & c.cmd(c.cmd'high downto 1) after delay; + f.op <= "0" & c.op (c.op'high downto 1) after delay; + end if; + end if; + + f.flags(Ng) <= c.acc(0) after delay; -- contains highest bit when 'last' is true + + -- NB. 'f.choice' may be overwritten for INDIRECT. + if c.flags(HLT) = '1' then + f.choice <= HALT after delay; + elsif c.flags(R) = '1' then + f.choice <= RESET after delay; + else + f.choice <= EXECUTE after delay; + end if; + -- INDIRECT is only used instruction allows for indirection + -- (ie. All those instructions in which the top bit is not set). + -- The indirection add 2*(N+1) cycles to the instruction so is quite expensive. + -- + -- We could avoid having this state and CPU functionality if we were to + -- make use of self-modifying code, however that would make programming the CPU + -- more difficult. + -- + when INDIRECT => + assert c.cmd(c.cmd'high) = '0' severity error; + f.choice <= EXECUTE after delay; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + else + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= "0" & c.op(c.op'high downto 1) after delay; + f.choice <= OPERAND after delay; + end if; + -- OPERAND fetches the operand *again*, this time using the operand + -- acquired in EXECUTE, the address being set in the previous INDIRECT state. + when OPERAND => + f.choice <= EXECUTE after delay; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + else + ie <= '1' after delay; + f.op <= i & c.op(c.op'high downto 1) after delay; + end if; + -- The EXECUTE state implements the ALU. It is the most seemingly the + -- most complex state, but it is not (FETCH is more difficult to + -- understand). + when EXECUTE => + assert not (c.flags(Z) = '1' and c.flags(Ng) = '1') report "zero and negative?"; + f.choice <= ADVANCE after delay; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + if cmd = iADD then f.flags(Cy) <= '0' after delay; end if; + -- 'tcarry' is added to the program counter in the ADVANCE + -- state, instructions that affect the program counter clear + -- it (such as iJUMP, and iJUMPZ/iSET (conditionally). + f.tcarry <= '1' after delay; + else + case cmd is -- ALU + when iOR => + f.op <= "0" & c.op (c.op'high downto 1) after delay; + f.acc <= (c.op(0) or c.acc(0)) & c.acc(c.acc'high downto 1) after delay; + when iAND => + f.acc <= c.acc(0) & c.acc(c.acc'high downto 1) after delay; + if (not c.last4) or c.indir then + f.op <= "0" & c.op (c.op'high downto 1) after delay; + f.acc <= (c.op(0) and c.acc(0)) & c.acc(c.acc'high downto 1) after delay; + end if; + when iXOR => + f.op <= "0" & c.op (c.op'high downto 1) after delay; + f.acc <= (c.op(0) xor c.acc(0)) & c.acc(c.acc'high downto 1) after delay; + when iADD => + f.acc <= "0" & c.acc(c.acc'high downto 1) after delay; + f.op <= "0" & c.op(c.op'high downto 1) after delay; + add1 <= c.acc(0) after delay; + add2 <= c.op(0) after delay; + acin <= c.flags(Cy) after delay; + f.acc(f.acc'high) <= ares after delay; + f.flags(Cy) <= acout after delay; + -- A barrel shifter is usually quite an expensive piece of hardware, + -- but it ends up being quite cheap for obvious reasons. If we really + -- needed to we could dispense with the right shift, we could mask off + -- low bits and rotate (either way) to emulate it. + when iLSHIFT => + if c.op(0) = '1' then + f.acc <= c.acc(c.acc'high - 1 downto 0) & "0" after delay; + end if; + f.op <= "0" & c.op (c.op'high downto 1) after delay; + when iRSHIFT => + if c.op(0) = '1' then + f.acc <= "0" & c.acc(c.acc'high downto 1) after delay; + end if; + f.op <= "0" & c.op (c.op'high downto 1) after delay; + -- We have two sets of LOAD/STORE instructions, one set which + -- optionally respects the indirect flag, and one set (the latter) + -- which never does. This allows us to perform direct LOAD/STORES + -- when the indirect flag is on. + when iLOAD => + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= c.op(0) & c.op(c.op'high downto 1) after delay; + f.choice <= LOAD after delay; + when iSTORE => + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= "0" & c.op(c.op'high downto 1) after delay; + f.choice <= STORE after delay; + when iLOADC => + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= c.op(0) & c.op(c.op'high downto 1) after delay; + f.choice <= LOAD after delay; + when iSTOREC => + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= "0" & c.op(c.op'high downto 1) after delay; + f.choice <= STORE after delay; + when iLITERAL => + f.acc <= c.op(0) & c.acc(c.acc'high downto 1) after delay; + f.op <= "0" & c.op (c.op'high downto 1) after delay; + when iUNUSED => + -- We could use this if we need to extend the instruction set + -- for any reason. I cannot think of a good one that justifies the + -- cost of a new instruction. So this will remain blank for now. + -- + -- Candidates for an instruction include: + -- + -- * Arithmetic Right Shift + -- * Subtraction + -- * Swap Low/High Byte (may be difficult to implement) + -- + -- However, this instruction may not have its indirection bit set, + -- This would not be a problem for the swap instruction. Alternatively + -- an 'add-constant' could be added. + -- + when iJUMP => + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= "0" & c.op(c.op'high downto 1) after delay; + f.pc <= c.op(0) & c.pc(c.pc'high downto 1) after delay; + f.choice <= FETCH after delay; + when iJUMPZ => + if c.flags(Z) = jumpz then + ae <= '1' after delay; + a <= c.op(0) after delay; + f.op <= "0" & c.op(c.op'high downto 1) after delay; + f.pc <= c.op(0) & c.pc(c.pc'high downto 1) after delay; + f.choice <= FETCH after delay; + end if; + -- NB. We could probably eliminate these instructions by mapping + -- the registers into the memory address space, this would free + -- up another two instructions, and potentially simplify the CPU. + -- + when iSET => + if c.op(0) = '0' then + -- NB. We could set the address directly here and + -- go to FETCH but that costs us too much time and gates. + f.pc <= c.acc(0) & c.pc(c.pc'high downto 1) after delay; + f.tcarry <= '0' after delay; + else + f.flags <= c.acc(0) & c.flags(c.flags'high downto 1) after delay; + end if; + f.acc <= c.acc(0) & c.acc(c.acc'high downto 1) after delay; + when iGET => + if c.op(0) = '0' then + f.acc <= c.pc(0) & c.acc(c.acc'high downto 1) after delay; + f.pc <= c.pc(0) & c.pc(c.pc'high downto 1) after delay; + else + f.acc <= c.flags(0) & c.acc(c.acc'high downto 1) after delay; + f.flags <= c.flags(0) & c.flags(c.flags'high downto 1) after delay; + end if; + end case; + end if; + -- Unfortunately we cannot perform a load or a store whilst we are + -- performing an EXECUTE, so we require STORE and LOAD states to do + -- more work after a LOAD or STORE instruction. + when STORE => + f.choice <= ADVANCE after delay; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + else + o <= c.acc(0) after delay; + oe <= '1' after delay; + f.acc <= c.acc(0) & c.acc(c.acc'high downto 1) after delay; + end if; + when LOAD => + f.choice <= ADVANCE after delay; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + else + ie <= '1' after delay; + f.acc <= i & c.acc(c.acc'high downto 1) after delay; + end if; + -- ADVANCE reuses our adder in iADD to add one to the program counter + -- this state *is* reached when we do a iJUMP, iJUMPZ or an iSET on the + -- program counter, those instructions clear the 'tcarry', which is + -- normally '1'. + when ADVANCE => + f.choice <= FETCH after delay; + if c.first then + f.dline(0) <= '1' after delay; + f.first <= false after delay; + else + f.pc <= "0" & c.pc(c.pc'high downto 1) after delay; + add1 <= c.pc(0) after delay; + -- A 'skip' facility could be made by optionally setting this to '1' + -- for the first cycle, incrementing the program counter by 2. + add2 <= '0' after delay; + acin <= c.tcarry after delay; + f.pc(f.pc'high) <= ares after delay; + a <= ares after delay; + f.tcarry <= acout after delay; + ae <= '1' after delay; + end if; + when HALT => stop <= '1' after delay; + end case; + end process; +end architecture; + Index: bitserial/trunk/makefile =================================================================== --- bitserial/trunk/makefile (nonexistent) +++ bitserial/trunk/makefile (revision 2) @@ -0,0 +1,156 @@ +CC=gcc +CFLAGS=-Wall -Wextra -std=c99 -O2 +USB?=/dev/ttyUSB0 +BAUD?=115200 +DIFF?=vimdiff +#BAUD?=9600 + +.PHONY: all run diff simulation viewer clean documentation + +all: bit simulation + +run: bit bit.hex + ./bit bit.hex + +talk: + picocom --omap delbs -e b -b ${BAUD} ${USB} + +simulation: tb.ghw + +viewer: tb.ghw signals.tcl + gtkwave -S signals.tcl -f $< > /dev/null 2>&1 & + +documentation: readme.htm + +%.htm: %.md + pandoc $< -o $@ + +bit.hex bit.bin: bit.fth bit + gforth $< + +bit: bit.c + ${CC} ${CFLAGS} $< -o $@ + +%.o: %.vhd + ghdl -a -g $< + +uart.o: uart.vhd util.o + +peripherals.o: peripherals.vhd uart.o util.o + +top.o: top.vhd peripherals.o bit.o util.o uart.o + +tb.o: tb.vhd bit.o peripherals.o top.o + +tb: tb.o bit.o peripherals.o top.o + ghdl -e $@ + +tb.ghw: tb tb.conf bit.hex + ghdl -r $< --wave=$<.ghw --max-stack-alloc=16384 --ieee-asserts=disable + +SOURCES = \ + top.vhd \ + bit.vhd \ + uart.vhd \ + util.vhd \ + peripherals.vhd + +OBJECTS = ${SOURCES:.vhd=.o} + +bitfile: design.bit + +reports: + @[ -d reports ] || mkdir reports +tmp: + @[ -d tmp ] || mkdir tmp +tmp/_xmsgs: + @[ -d tmp/_xmsgs ] || mkdir tmp/_xmsgs + +tmp/top.prj: tmp + @rm -f tmp/top.prj + @( \ + for f in $(SOURCES); do \ + echo "vhdl work \"$$f\""; \ + done; \ + echo "vhdl work \"top.vhd\"" \ + ) > tmp/top.prj + +tmp/top.lso: tmp + @echo "work" > tmp/top.lso + +tmp/top.xst: tmp tmp/_xmsgs tmp/top.lso tmp/top.lso + @( \ + echo "set -tmpdir \"tmp\""; \ + echo "set -xsthdpdir \"tmp\""; \ + echo "run"; \ + echo "-lso tmp/top.lso"; \ + echo "-ifn tmp/top.prj"; \ + echo "-ofn top"; \ + echo "-p xc6slx16-csg324-3"; \ + echo "-top top"; \ + echo "-opt_mode area"; \ + echo "-opt_level 2" \ + ) > tmp/top.xst + +synthesis: bit.hex reports tmp tmp/_xmsgs tmp/top.prj tmp/top.xst + @echo "Synthesis running..." + @${TIME} xst -intstyle silent -ifn tmp/top.xst -ofn reports/xst.log + @mv _xmsgs/* tmp/_xmsgs + @rmdir _xmsgs + @mv top_xst.xrpt tmp + @grep "ERROR\|WARNING" reports/xst.log | \ + grep -v "WARNING.*has a constant value.*This FF/Latch will be trimmed during the optimization process." | \ + cat + @grep ns reports/xst.log | grep 'Clock period' + +implementation: reports tmp + @echo "Implementation running..." + + @[ -d tmp/xlnx_auto_0_xdb ] || mkdir tmp/xlnx_auto_0_xdb + + @${TIME} ngdbuild -intstyle silent -quiet -dd tmp -uc top.ucf -p xc6slx16-csg324-3 top.ngc top.ngd + @mv top.bld reports/ngdbuild.log + @mv _xmsgs/* tmp/_xmsgs + @rmdir _xmsgs + @mv xlnx_auto_0_xdb/* tmp + @rmdir xlnx_auto_0_xdb + @mv top_ngdbuild.xrpt tmp + + @${TIME} map -intstyle silent -detail -p xc6slx16-csg324-3 -convert_bram8 -pr b -c 100 -w -o top_map.ncd top.ngd top.pcf + @mv top_map.mrp reports/map.log + @mv _xmsgs/* tmp/_xmsgs + @rmdir _xmsgs + @mv top_usage.xml top_summary.xml top_map.map top_map.xrpt tmp + + @${TIME} par -intstyle silent -w -ol std top_map.ncd top.ncd top.pcf + @mv top.par reports/par.log + @mv top_pad.txt reports/par_pad.txt + @mv _xmsgs/* tmp/_xmsgs + @rmdir _xmsgs + @mv par_usage_statistics.html top.ptwx top.pad top_pad.csv top.unroutes top.xpi top_par.xrpt tmp + +design.bit: reports tmp/_xmsgs + @echo "Generate bitfile running..." + @touch webtalk.log + @${TIME} bitgen -intstyle silent -w top.ncd + @mv top.bit design.bit + @mv top.bgn reports/bitgen.log + @mv _xmsgs/* tmp/_xmsgs + @rmdir _xmsgs + @sleep 5 + @mv top.drc top_bitgen.xwbt top_usage.xml top_summary.xml webtalk.log tmp + @grep -i '\(warning\|clock period\)' reports/xst.log + +upload: + djtgcfg prog -d Nexys3 -i 0 -f design.bit + +design: clean simulation synthesis implementation bitfile + +postsyn: + @netgen -w -ofmt vhdl -sim ${NETLIST}.ngc post_synthesis.vhd + @netgen -w -ofmt vhdl -sim ${NETLIST}.ngd post_translate.vhd + @netgen -pcf ${NETLIST}.pcf -w -ofmt vhdl -sim ${NETLIST}.ncd post_map.vhd + +clean: + git clean -fdx . + Index: bitserial/trunk/peripherals.vhd =================================================================== --- bitserial/trunk/peripherals.vhd (nonexistent) +++ bitserial/trunk/peripherals.vhd (revision 2) @@ -0,0 +1,204 @@ +-- File: peripherals.vhd +-- Author: Richard James Howe +-- Repository: https://github.com/howerj/bit-serial +-- License: MIT +-- Description: Memory and Memory mapped peripherals + +library ieee, work, std; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; +use work.util.all; +use work.uart_pkg.all; + +entity peripherals is + generic ( + g: common_generics; + file_name: string; + baud: positive; + W: positive; + N: positive; + uart_fifo_depth: natural; + uart_use_cfg: boolean); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + rx: in std_ulogic; + tx: out std_ulogic; + ld: out std_ulogic_vector(7 downto 0); + sw: in std_ulogic_vector(7 downto 0); + i, a: in std_ulogic; + o: out std_ulogic; + oe, ie, ae: in std_ulogic); +end; + +architecture rtl of peripherals is + constant data_length: positive := N; + constant addr_length: positive := W; + + type registers_t is record + r_a: std_ulogic_vector(N - 1 downto 0); + r_i: std_ulogic_vector(N - 1 downto 0); + r_o: std_ulogic_vector(N - 1 downto 0); + r_ld: std_ulogic_vector(ld'range); + r_ie: std_ulogic; + end record; + + constant registers_default: registers_t := ( + r_a => (others => '0'), + r_i => (others => '0'), + r_o => (others => '0'), + r_ld => (others => '0'), + r_ie => '0' + ); + + signal c, f: registers_t := registers_default; + + signal io, write: boolean := false; + signal dwe, dre: std_ulogic := '0'; + signal dout: std_ulogic_vector(N - 1 downto 0) := (others => '0'); + + signal tx_fifo_full: std_ulogic; + signal tx_fifo_empty: std_ulogic; + signal tx_fifo_we: std_ulogic; + signal tx_fifo_data: std_ulogic_vector(7 downto 0); + + signal rx_fifo_full: std_ulogic; + signal rx_fifo_empty: std_ulogic; + signal rx_fifo_re: std_ulogic; + signal rx_fifo_data: std_ulogic_vector(7 downto 0); + + signal reg: std_ulogic_vector(15 downto 0); + signal clock_reg_tx_we: std_ulogic; + signal clock_reg_rx_we: std_ulogic; + signal control_reg_we: std_ulogic; + + signal io_addr: std_ulogic_vector(2 downto 0); +begin + io <= c.r_a(c.r_a'high - 1) = '1' and ae = '0' after g.delay; + io_addr <= c.r_a(io_addr'range) after g.delay; + write <= true when (c.r_ie and (c.r_ie xor f.r_ie)) = '1' else false after g.delay; + ld <= c.r_ld after g.delay; + o <= c.r_o(0) after g.delay; + tx_fifo_data <= c.r_i(tx_fifo_data'range) after g.delay; + reg <= c.r_i(reg'range) after g.delay; + + uart: entity work.uart_top + generic map ( + clock_frequency => g.clock_frequency, + delay => g.delay, + asynchronous_reset => g.asynchronous_reset, + baud => baud, + fifo_depth => uart_fifo_depth, + use_cfg => uart_use_cfg) + port map( + clk => clk, rst => rst, + + tx => tx, + tx_fifo_full => tx_fifo_full, + tx_fifo_empty => tx_fifo_empty, + tx_fifo_we => tx_fifo_we, + tx_fifo_data => tx_fifo_data, + + rx => rx, + rx_fifo_full => rx_fifo_full, + rx_fifo_empty => rx_fifo_empty, + rx_fifo_re => rx_fifo_re, + rx_fifo_data => rx_fifo_data, + + reg => reg, + clock_reg_tx_we => clock_reg_tx_we, + clock_reg_rx_we => clock_reg_rx_we, + control_reg_we => control_reg_we); + + bram: entity work.single_port_block_ram + generic map( + g => g, + file_name => file_name, + file_type => FILE_HEX, + addr_length => addr_length, + data_length => data_length) + port map ( + clk => clk, + dwe => dwe, + addr => f.r_a(addr_length - 1 downto 0), + dre => dre, + din => f.r_i, + dout => dout); + + process (clk, rst) + begin + if rst = '1' and g.asynchronous_reset then + c <= registers_default after g.delay; + elsif rising_edge(clk) then + if rst = '1' and not g.asynchronous_reset then + c <= registers_default after g.delay; + else + c <= f after g.delay; + end if; + end if; + end process; + + process (c, i, a, oe, ie, ae, dout, io, write, sw, rx, io_addr, + rx_fifo_data, rx_fifo_empty, rx_fifo_full, tx_fifo_empty, tx_fifo_full) + begin + f <= c after g.delay; + f.r_o <= dout after g.delay; + f.r_ie <= ie after g.delay; + dre <= '1' after g.delay; + dwe <= '0' after g.delay; + tx_fifo_we <= '0' after g.delay; + rx_fifo_re <= '0' after g.delay; + clock_reg_tx_we <= '0' after g.delay; + clock_reg_rx_we <= '0' after g.delay; + control_reg_we <= '0' after g.delay; + + if ae = '1' then f.r_a <= a & c.r_a(c.r_a'high downto 1) after g.delay; end if; + if oe = '1' then f.r_o <= c.r_o(0) & c.r_o(c.r_o'high downto 1) after g.delay; end if; + if ie = '1' then f.r_i <= i & c.r_i(c.r_i'high downto 1) after g.delay; end if; + + if oe = '0' and ae = '0' then + if io = false then + dre <= '1' after g.delay; + else + f.r_o <= (others => '0') after g.delay; + case io_addr is + when "000" => f.r_o(sw'range) <= sw after g.delay; + when "001" => + f.r_o(7 downto 0) <= rx_fifo_data after g.delay; + f.r_o(8) <= rx_fifo_empty after g.delay; + f.r_o(9) <= rx_fifo_full after g.delay; + f.r_o(11) <= tx_fifo_empty after g.delay; + f.r_o(12) <= tx_fifo_full after g.delay; + when "010" => + when "011" => + when "100" => + when "101" => + when "110" => + when "111" => + when others => + end case; + end if; + end if; + + if write and ae = '0' then + if io = false then + dwe <= '1' after g.delay; + else + case io_addr is + when "000" => f.r_ld <= c.r_i(c.r_ld'range) after g.delay; + when "001" => tx_fifo_we <= c.r_i(13) after g.delay; + rx_fifo_re <= c.r_i(10) after g.delay; + when "010" => if uart_use_cfg then clock_reg_tx_we <= '1' after g.delay; end if; + when "011" => if uart_use_cfg then clock_reg_rx_we <= '1' after g.delay; end if; + when "100" => if uart_use_cfg then control_reg_we <= '1' after g.delay; end if; + when "101" => + when "110" => + when "111" => + when others => + end case; + end if; + end if; + end process; +end architecture; + Index: bitserial/trunk/readme.md =================================================================== --- bitserial/trunk/readme.md (nonexistent) +++ bitserial/trunk/readme.md (revision 2) @@ -0,0 +1,574 @@ +# BIT SERIAL CPU and TOOL-CHAIN + +* Project: Bit-Serial CPU in VHDL +* Author: Richard James Howe +* Copyright: 2019,2020 Richard James Howe +* License: MIT +* Email: howe.r.j.89@gmail.com +* Website: + +*Processing data one bit at a time, since 2019*. + +# Introduction + +This is a project for a [bit-serial CPU][], which is a CPU that has an architecture +which processes a single bit at a time instead of in parallel like a normal +CPU. This allows the CPU itself to be a lot smaller, the penalty is that it is +*a lot* slower. The CPU itself is called *bcpu*. + +The CPU is incredibly basic, lacking features required to support +higher level programming (such as function calls). Instead such features can +be emulated if they are needed. If such features are needed, or faster +throughput (whilst still remaining quite small) other [Soft-Core][] CPUs are +available, such as the [H2][]. + +To build and run the C based simulator for the project, you will need a C +compiler and 'make'. To build and run the [VHDL][] simulator, you will need [GHDL][] +installed. + +The cross compiler requires [gforth][], although a pre-compiled image is +provided in case you do not have access to it, called '[bit.hex][]', this hex file +contains a working [Forth][] image. To run this: + + make bit + ./bit bit.hex + +An example session of the simulator running is: + +![C Simulator Running eForth](bit-sim.gif) + +You should be greeted by a [Forth][] prompt, type 'words' and hit a carriage +return to get a list of defined functions. + +The target [FPGA][] that the system is built for is a [Spartan-6][], for a +[Nexys 3][] development board. [Xilinx ISE 14.7][] was used to build the +project. + +The following 'make' targets are available: + + make + +By default the [VHDL][] test bench is built and simulated in [GHDL][]. This +requires [gforth][] to assemble the test program [bit.fth][] into a file +readable by the simulator. + + make run + +This target builds the C based simulator, assembles the test program +and runs the simulator on the assembled program. + + make synthesis implementation bitfile + +This builds the project for the [FPGA][]. + + make upload + +This uploads the project to the [Nexys 3][] board. This requires that +'djtgcfg' is installed, which is a tool provided by [Digilent][]. + + make documentation + +This turns this 'readme.md' file into a HTML file. + + make clean + +Cleans up the project. + +# eForth + +The tool-chain for the device is used to build an image for a Forth +interpreter, more specifically a Forth interpreter similar to a dialect of +Forth known as 'eForth', it differs between eForth in order to save on space +which is at a premium. You should be greeted with an eForth prompt when running +the 'make run' target that looks something like this: + + $ make run + ./bit bit.hex + eForth 3.1 + +You can see all of the defined words (or functions) by typing in 'words' and +hitting return. + + $ make run + ./bit bit.hex + eForth 3.1 + words + +Arithmetic in Forth in done using Reverse Polish Notation: + + 2 2 + . cr + +Will print out '4'. This is not the place for a Forth tutorial, the Forth +interpreter is mainly here to demonstrate that the bit-serial CPU is working +correctly and can be used for useful purposes. No demonstration would be +complete without a 'Hello, World' program, however: + + : hello cr ." Hello, World!" ; + hello + +Go use your favorite search engine to find a Forth tutorial. + +# Use Case + +Often in an [FPGA][] design there is spare Dual Port Block RAM (BRAM) available, +either because only part of the BRAM module is being used or because it is not +needed entirely. Adding a new CPU however is a bigger decision than using spare +BRAM capacity, it can take up quite a lot of floor space, and perhaps other +precious resources. If this is the case then adding this CPU costs practically +nothing in terms of floor space, the main cost will be in development time. + +In short, the project may be useful if: + +* FPGA Floor space is at a premium in your design. +* You have spare memory for the program and storage. +* You need a programmable CPU that supports a reasonable instruction set. +* *Execution speed is not a concern*. + +There were two use cases that the author had in mind when setting out to build +this system: + +* As a CPU driving a low-baud UART +* As a controller for a VT100 terminal emulator that would control cursor + position and parse escape codes, setting colors and attributes in a hardware + based text-terminal (this was to replace an existing VHDL only system that + had spare capacity in the FPGAs dual-port block RAMs used to store the Font + and text). + +# Tool-chain + +The tool-chain consists of a cross compiler written in Forth, it itself +implements a virtual machine on top of which a Forth interpreter is written. +The accumulator machine lacks call/returns, and a stack, so these have to be +implemented. The meta-compiler (a Forth specific term for what is a +more widely known as a cross-compiler) is available in [bit.fth][]. + +As the instruction set is anemic and CPU features lacking it is best to target +the virtual machine and program in Forth than it is to program in assembly. + +Despite the inherently slow speed of the design and the further slow down +executing code on top of a virtual machine the interpreter is plenty fast +enough for interactive use, slowing down noticeably when division has to be +performed. + +# CPU Specification + +The CPU is a 16-bit design, in principle a normal bit parallel CPU design could +be implemented of the same CPU, but in practice you not end up with a CPU like +this one if you remove the bit-serial restriction. + +The CPU has 16 operation, each instruction consists of a 4-bit operation field +and a 12-bit operand. Depending on the CPU mode that operand and instruction +that operand can either be a literal or an address to load a 16-bit word from +(addresses are word and not byte oriented, so the lowest bit of an address +specifies the next word not byte). Only the first 8 operations can have their +operand indirected, which is deliberate. + +The CPU is an accumulator machine, all instructions either modify or use the +accumulator to store operation results in them. The CPU has three registers +including the accumulator, the other two are the program counter which is +automatically incremented after each instruction excluding the jump +instructions (the SET instruction is also excluded when setting the program +counter only) and a flags register. + +The instructions are: + + | ----------- | -------------------------------------- | --------------------------------- | ---------------- | + | Instruction | C Operation | Description | Cycles | + | ----------- | -------------------------------------- | --------------------------------- | ---------------- | + | OR | acc |= lop | Bitwise Or | [3 or 5]*(N+1) | + | AND | acc &= lop | Bitwise And | [3 or 5]*(N+1) | + | XOR | acc ^= lop | Bitwise Exclusive Or | [3 or 5]*(N+1) | + | ADD | acc += lop | Add with carry, sets carry | [3 or 5]*(N+1) | + | LSHIFT | acc = acc << lop (or rotate left) | Shift left or Rotate left | [3 or 5]*(N+1) | + | RSHIFT | acc = acc >> lop (or rotate right) | Shift right or Rotate right | [3 or 5]*(N+1) | + | LOAD | acc = memory(lop) | Load | [4 or 6]*(N+1) | + | STORE | memory(lop) = acc | Store | [4 or 6]*(N+1) | + | LOADC | acc = memory(op) | Load from memory constant addr | 4*(N+1) | + | STOREC | memory(op) = acc | Store to memory constant addr | 4*(N+1) | + | LITERAL | acc = op | Load literal into accumulator | 3*(N+1) | + | UNUSED | N/A | Unused instruction | 3*(N+1) | + | JUMP | pc = op | Unconditional Jump | 2*(N+1) | + | JUMPZ | if(!acc){pc = op } | Jump If Zero | [2 or 3]*(N+1) | + | SET | if(op&1){flg=acc}else{pc=acc} | Set Register | 3*(N+1) | + | GET | if(op&1){acc=flg}else{acc=pc} | Get Register | 3*(N+1) | + | ----------- | -------------------------------------- | --------------------------------- | ---------------- | + +* pc = program counter +* acc = accumulator +* indir = indirect flag +* lop = instruction operand if indirect flag not set, otherwise it equals to the memory + location pointed to by the operand +* op = instruction operand +* flg = flags register +* N = bit width, which is 16. + +The number of cycles an instruction takes to complete depends on whether it +performs an indirection, or in the case of GET/SET it depends if it it setting +the program counter (2 cycles only) or the flags register (3 cycles), or performing +an I/O operation (4 cycles), getting the flags or program counter always costs +3 cycles. + +The flags in the 'flg' register are: + + | ---- | --- | --------------------------------------- | + | Flag | Bit | Description | + | ---- | --- | --------------------------------------- | + | Cy | 0 | Carry flag, set by addition instruction | + | Z | 1 | Zero flag | + | Ng | 2 | Negative flag | + | R | 3 | Reset Flag - Resets the CPU | + | HLT | 4 | Halt Flag - Stops the CPU | + | ---- | --- | --------------------------------------- | + +* The carry flag (Cy) is set by the ADD instruction, it can also be set and cleared +with the GET/SET instructions. +* 'Z' is set whenever the accumulator is zero. +* 'Ng' is set whenever the accumulator has its highest bit set, indicating that + the accumulator is negative. +* 'R', Reset flag, this resets the CPU immediately, only the HLT flag takes +precedence. +* 'HLT', The halt flag takes priority over everything else, sending the CPU +into a halt state. + +There is really not much else to this CPU from the point of view of a user of +this core, integrating this core into another system is more complicated +however, you will need to be far more aware of timing of signals and their +enable lines. Much like the processor, a single bit bus in conjunction with an +enable is used to communicate with the outside world. + +The internal state of the CPU is minimal, to make a working system the memory +and I/O controller will need (shift) registers to store the address and +input/output. + +The CPU state-machine is: + +![CPU State Machine](bit-state.png) + +And the CPU bus timing diagram: + +![CPU Bus timing](bit-wave.png) + + +# Peripherals + +The system has a minimal set of peripherals; a bank of switches with LEDs next +to each switch and a UART capable of transmission and reception, other +peripherals could be added as needed. + +## Register Map + +The I/O register map for the device is very small as there are very few +peripherals. + + | ------- | -------------- | + | Address | Name | + | ------- | -------------- | + | 0x4000 | LED/Switches | + | 0x4001 | UART TX/RX | + | 0x4002 | UART Clock TX* | + | 0x4003 | UART Clock RX* | + | 0x4004 | UART Control* | + | ------- | -------------- | + These registers are turned off by default + and will need to be enabled during synthesis. + +* LED/Switches + +A bank of switches, non-debounced, with LED lights next to them. + + +---------------------------------------------------------------+ + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +---------------------------------------------------------------+ + | | Switches 1 = on, 0 = off | READ + +---------------------------------------------------------------+ + | | LED 1 = on, 0 = off | WRITE + +---------------------------------------------------------------+ + +* UART TX/RX + +The UART TX/RX register is used to read and write data bytes to the UART and +check on the UART status. The UART has a FIFO that is used to capture the +results of the UART. The usage of which is non-optional. + + +---------------------------------------------------------------+ + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +---------------------------------------------------------------+ + | |TFF|TFE| |RFF|RFE| RX DATA BYTE | READ + +---------------------------------------------------------------+ + | |TFW| |RFR| | TX DATA BYTE | WRITE + +---------------------------------------------------------------+ + RFE = RX FIFO EMPTY + RFF = RX FIFO FULL + RFR = RX FIFO READ ENABLE + TFE = TX FIFO EMPTY + TFF = TX FIFO FULL + TFW = TX FIFO WRITE ENABLE + +* UART Clock TX + +The UART Transmission clock, independent from the Reception Clock, is +controllable via this register. + +Defaults are: 115200 Baud + + +---------------------------------------------------------------+ + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +---------------------------------------------------------------+ + | | READ + +---------------------------------------------------------------+ + | UART TX CLOCK DIVISOR | WRITE + +---------------------------------------------------------------+ + +* UART Clock RX + +The UART Reception clock, independent from the Transmission Clock, is +controllable via this register. + +Defaults are: 115200 Baud + + +---------------------------------------------------------------+ + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +---------------------------------------------------------------+ + | | READ + +---------------------------------------------------------------+ + | UART RX CLOCK DIVISOR | WRITE + +---------------------------------------------------------------+ + +* UART Clock Control + +This clock is used to control UART options such as the number of bits, + +Defaults are: 8N1, no parity + + +---------------------------------------------------------------+ + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +---------------------------------------------------------------+ + | | READ + +---------------------------------------------------------------+ + | | DATA BITS |STPBITS|EPA|UPA| WRITE + +---------------------------------------------------------------+ + UPA = USE PARITY BITS + EPA = EVEN PARITY + STPBITS = Number of stop bits + DATA BITS = Number of data bits + + +# Other Soft Microprocessors + +This is a *very* specialized core, that cannot be emphasized enough. It +executes slowly, but is small. Other, larger core (but still relatively small) +may be useful for your needs. In terms of engineering trade offs this design +takes things to the extreme in one direction only. + +The core should be written to be portable to different [FPGA][]s, however the +author only tests what they have available (Xilinx, Spartan-6). + +* The H2 + +Another small core, based on the J1. This core executes quite quickly (1 +instruction per CPU cycle) and uses +few resources, although much more than this core. The instruction set is quite +dense and allows for higher level programming than just using straight +assembler. See . + +This CPU core has deeper stacks, more instructions, and interrupts, which the +original J1 core lacks. It is also written in VHDL instead of Verilog. + +* Tiny CPU in a CPLD + +This is a 8-bit CPU designed to fit in the limited resources of a CPLD: + +See and +. + +It is written in Verilog, it is based on the 6502, implementing a subset of its +instructions. It is probably easier to directly program than this bit-serial +CPU, and roughly the same size (although a direct comparison is difficult). +It can address less memory (1K) without bank-switching. There is also a +different version made with 7400 series logic gates +. + +* Leros and Lipsi + +See , +also , + +# References / Appendix + +The state-machine diagram was made using [Graphviz][], and can be viewed and +edited immediately by copying the following text into [GraphvizOnline][]. + + + digraph bcpu { + reset -> fetch [label="start"] + fetch -> execute + fetch -> indirect [label="flag(IND) = '1'\n and op < 8"] + fetch -> reset [label="flag(RST) = '1'"] + fetch -> halt [label="flag(HLT) = '1'"] + indirect -> operand + operand -> execute + execute -> advance + execute -> store [label="op = 'store'"] + execute -> load [label="op = 'load'"] + execute -> fetch [label="(op = 'jumpz' and acc = 0)\n or op ='jump'"] + store -> advance + load -> advance + advance -> fetch + halt -> halt + } + + +For timing diagrams, use [Wavedrom][] with the following text: + + + {signal: [ + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2..................', data: ['HALT']}, + {name: 'ie', wave: 'x0..................'}, + {name: 'oe', wave: 'x0..................'}, + {name: 'ae', wave: 'x0..................'}, + {name: 'o', wave: 'x0..................'}, + {name: 'i', wave: 'x...................'}, + {name: 'halt', wave: 'x1..................'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['ADVANCE']}, + {name: 'ie', wave: 'x0.................x'}, + {name: 'oe', wave: 'x0.................x'}, + {name: 'ae', wave: 'x01...............0x'}, + {name: 'o', wave: 'x0================0x', data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', 'F12', 'F13', 'F14', 'F15']}, + {name: 'i', wave: 'x.................xx'}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['OPERAND or LOAD']}, + {name: 'ie', wave: 'x01...............0x'}, + {name: 'oe', wave: 'x0.................x'}, + {name: 'ae', wave: 'x0.................x'}, + {name: 'o', wave: 'x0.................x'}, + {name: 'i', wave: 'x.================xx', data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['STORE']}, + {name: 'ie', wave: 'x0.................x'}, + {name: 'oe', wave: 'x01...............0x'}, + {name: 'ae', wave: 'x0.................x'}, + {name: 'o', wave: 'x0================0x', data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']}, + {name: 'i', wave: 'x.................xx'}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['INDIRECT or EXECUTE: LOAD, STORE, JUMP, JUMPZ']}, + {name: 'ie', wave: 'x0.................x'}, + {name: 'oe', wave: 'x0.................x'}, + {name: 'ae', wave: 'x01...............0x'}, + {name: 'o', wave: 'x0================0x', data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', 'F12', 'F13', 'F14', 'F15']}, + {name: 'i', wave: 'x.................xx'}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['EXECUTE: NORMAL INSTRUCTION']}, + {name: 'ie', wave: 'x0.................x'}, + {name: 'oe', wave: 'x0.................x'}, + {name: 'ae', wave: 'x0.................x'}, + {name: 'o', wave: 'x0.................x'}, + {name: 'i', wave: 'x.................xx'}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['FETCH']}, + {name: 'ie', wave: 'x01...............0x'}, + {name: 'oe', wave: 'x0.................x'}, + {name: 'ae', wave: 'x0.................x'}, + {name: 'o', wave: 'x0.................x'}, + {name: 'i', wave: 'x.================xx', data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + {name: 'clk', wave: 'pp...p...p...p...p..'}, + {name: 'cycle', wave: '22222222222222222222', data: ['prev', 'init','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', 'next', 'rest']}, + {name: 'cmd', wave: 'x2................xx', data: ['RESET']}, + {name: 'ie', wave: 'x0.................x'}, + {name: 'oe', wave: 'x0.................x'}, + {name: 'ae', wave: 'x01...............0x'}, + {name: 'o', wave: 'x0.................x'}, + {name: 'i', wave: 'x.................xx'}, + {name: 'halt', wave: 'x0.................x'}, + {}, + + ]} + + +That's all folks! + +[C]: https://en.wikipedia.org/wiki/C_%28programming_language%29 +[Digilent]: https://store.digilentinc.com/ +[FPGA]: https://en.wikipedia.org/wiki/Field-programmable_gate_array +[Forth]: https://www.forth.com/forth/ +[GHDL]: http://ghdl.free.fr/ +[GraphvizOnline]: https://dreampuf.github.io/GraphvizOnline +[Graphviz]: https://graphviz.org/ +[H2]: https://github.com/howerj/forth-cpu +[Nexys 3]: https://store.digilentinc.com/nexys-3-spartan-6-fpga-trainer-board-limited-time-see-nexys4-ddr/ +[Soft-Core]: https://en.wikipedia.org/wiki/Soft_microprocessor#Core_comparison +[Spartan-6]: https://www.xilinx.com/products/silicon-devices/fpga/spartan-6.html +[VHDL]: https://en.wikipedia.org/wiki/VHDL +[Wavedrom]: https://wavedrom.com/editor.html +[Xilinx ISE 14.7]: https://www.xilinx.com/products/design-tools/ise-design-suite/ise-webpack.html +[bit-serial CPU]: https://en.wikipedia.org/wiki/Bit-serial_architecture +[bit.c]: bit.c +[bit.fth]: bit.fth +[bit.fth]: bit.fth +[bit.hex]: bit.hex +[bit.vhd]: bit.vhd +[gforth]: https://gforth.org/ + + + Index: bitserial/trunk/signals.tcl =================================================================== --- bitserial/trunk/signals.tcl (nonexistent) +++ bitserial/trunk/signals.tcl (revision 2) @@ -0,0 +1,36 @@ +# Richard James Howe +# TCL Script for GTKWave on tb.ghw +# + +set bits 15 + +gtkwave::/Edit/Set_Trace_Max_Hier 0 +gtkwave::/Time/Zoom/Zoom_Amount -27.0 +gtkwave::/View/Show_Filled_High_Values 1 +gtkwave::setFromEntry 170ns + +# top.tb.uut.cpu.c.first +set names { + top.tb.rst + top.tb.clk + top.tb.uut.cpu.last + top.tb.uut.cpu.c.state + top.tb.uut.cpu.c.pc[15:0] + top.tb.uut.cpu.cmd + top.tb.uut.cpu.c.op[15:0] + top.tb.uut.cpu.c.acc[15:0] + top.tb.uut.cpu.ae + top.tb.uut.cpu.ie + top.tb.uut.cpu.oe +} + +gtkwave::addSignalsFromList $names + +foreach v $names { + set a [split $v .] + set a [lindex $a end] + gtkwave::highlightSignalsFromList $v + gtkwave::/Edit/Alias_Highlighted_Trace $a + gtkwave::/Edit/UnHighlight_All $a +} + Index: bitserial/trunk/tb.conf =================================================================== --- bitserial/trunk/tb.conf (nonexistent) +++ bitserial/trunk/tb.conf (revision 2) @@ -0,0 +1,3 @@ +12288 +0 +0 Index: bitserial/trunk/tb.vhd =================================================================== --- bitserial/trunk/tb.vhd (nonexistent) +++ bitserial/trunk/tb.vhd (revision 2) @@ -0,0 +1,104 @@ +-- File: tb.vhd +-- Author: Richard James Howe +-- Repository: https://github.com/howerj/bit-serial +-- License: MIT +-- Description: Test bench for top level entity + +library ieee, work, std; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.util.all; +use std.textio.all; + +entity tb is +end tb; + +architecture testing of tb is + constant g: common_generics := default_settings; + constant clock_period: time := 1000 ms / g.clock_frequency; + constant baud: positive := 115200 * 10; -- speed up TX/RX for simulation + shared variable clocks: integer := 10000; + shared variable forever: integer := 0; + shared variable debug: integer := 1; + constant N: positive := 16; + + signal ld: std_ulogic_vector(7 downto 0) := (others => '0'); + signal sw: std_ulogic_vector(7 downto 0) := x"AA"; + signal stop: boolean := false; + signal clk: std_ulogic := '0'; + signal halt: std_ulogic := '0'; + signal rst: std_ulogic := '1'; + signal tx, rx: std_ulogic := '0'; + + impure function configure(the_file_name: in string) return boolean is + file in_file: text is in the_file_name; + variable in_line: line; + variable i: integer; + begin + if endfile(in_file) then return false; end if; + readline(in_file, in_line); read(in_line, i); + clocks := i; + readline(in_file, in_line); read(in_line, i); + forever := i; + readline(in_file, in_line); read(in_line, i); + debug := i; + return true; + end function; + + signal configured: boolean := configure("tb.conf"); +begin + uut: entity work.top + generic map( + g => g, + file_name => "bit.hex", + N => N, + baud => baud, + debug => debug) + port map ( + clk => clk, +-- rst => rst, + halt => halt, + ld => ld, + sw => sw, + tx => tx, + rx => rx); + + clock_process: process + variable count: integer := 0; + variable ll: line; + + begin + rst <= '1'; + stop <= false; + wait for clock_period; + rst <= '0'; + while (count < clocks or forever /= 0) and halt = '0' loop + clk <= '1'; + wait for clock_period / 2; + clk <= '0'; + wait for clock_period / 2; + count := count + 1; + end loop; + if halt = '1' then + write(ll, string'("{HALT}")); + else + write(ll, string'("{CYCLES}")); + end if; + + if debug > 0 then + writeline(OUTPUT, ll); + end if; + + stop <= true; + wait; + end process; + + stimulus_process: process + begin + while stop = false loop + wait for clock_period; + end loop; + wait; + end process; +end architecture; + Index: bitserial/trunk/top.ucf =================================================================== --- bitserial/trunk/top.ucf (nonexistent) +++ bitserial/trunk/top.ucf (revision 2) @@ -0,0 +1,281 @@ +# Nexys3 board UCF file: Cut down version +# On Board 100 MHz Clock +# + +#Clock signal +Net "clk" LOC=V10 | IOSTANDARD=LVCMOS33; +Net "clk" TNM_NET = sys_clk_pin; +TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 100000 kHz; + +# # Input: Buttons +# Net "btnu" LOC = A8 | IOSTANDARD = LVCMOS33; button up +# Net "btnd" LOC = C9 | IOSTANDARD = LVCMOS33; button down +# Net "btnc" LOC = B8 | IOSTANDARD = LVCMOS33; button centre +# Net "btnl" LOC = C4 | IOSTANDARD = LVCMOS33; button left +# Net "btnr" LOC = D9 | IOSTANDARD = LVCMOS33; button right +# +# Input: Switches +Net "sw<0>" LOC = T10 | IOSTANDARD = LVCMOS33; # switch 0 +Net "sw<1>" LOC = T9 | IOSTANDARD = LVCMOS33; # switch 1 +Net "sw<2>" LOC = V9 | IOSTANDARD = LVCMOS33; # switch 2 +Net "sw<3>" LOC = M8 | IOSTANDARD = LVCMOS33; # switch 3 +Net "sw<4>" LOC = N8 | IOSTANDARD = LVCMOS33; # switch 4 +Net "sw<5>" LOC = U8 | IOSTANDARD = LVCMOS33; # switch 5 +Net "sw<6>" LOC = V8 | IOSTANDARD = LVCMOS33; # switch 6 +Net "sw<7>" LOC = T5 | IOSTANDARD = LVCMOS33; # switch 7 +# +# # Output: Individual LEDs +Net "ld<0>" LOC = U16 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 0 +Net "ld<1>" LOC = V16 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 1 +Net "ld<2>" LOC = U15 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 2 +Net "ld<3>" LOC = V15 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 3 +Net "ld<4>" LOC = M11 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 4 +Net "ld<5>" LOC = N11 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 5 +Net "ld<6>" LOC = R11 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 6 +Net "ld<7>" LOC = T11 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # led 7 +# +# # Output: 7/8 Segment LEDs +# Net "an<0>" LOC = P17 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # anode 0 +# Net "an<1>" LOC = P18 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # anode 1 +# Net "an<2>" LOC = N15 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # anode 2 +# Net "an<3>" LOC = N16 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # anode 3 +# +# Net "ka<0>" LOC = T17 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 0 - CA +# Net "ka<1>" LOC = T18 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 1 - CB +# Net "ka<2>" LOC = U17 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 2 - CC +# Net "ka<3>" LOC = U18 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 3 - CD +# Net "ka<4>" LOC = M14 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 4 - CE +# Net "ka<5>" LOC = N14 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 5 - CF +# Net "ka<6>" LOC = L14 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 6 - CG +# Net "ka<7>" LOC = M13 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # cathode 7 - DP + +# UART serial interface +Net "rx" LOC = N17 | IOSTANDARD = LVCMOS33; # uart rx +Net "tx" LOC = N18 | IOSTANDARD = LVCMOS33 | SLEW = FAST; # uart tx + +# VGA +# Net "o_vga_red<0>" LOC = U7 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L43P, Sch name = RED0 +# Net "o_vga_red<1>" LOC = V7 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L43N, Sch name = RED1 +# Net "o_vga_red<2>" LOC = N7 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L44P, Sch name = RED2 +# Net "o_vga_green<0>" LOC = P8 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L44N, Sch name = GRN0 +# Net "o_vga_green<1>" LOC = T6 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L45P, Sch name = GRN1 +# Net "o_vga_green<2>" LOC = V6 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L45N, Sch name = GRN2 +# Net "o_vga_blue<0>" LOC = R7 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L46P, Sch name = BLU1 +# Net "o_vga_blue<1>" LOC = T7 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L46N, Sch name = BLU2 +# Net "o_vga_hsync" LOC = N6 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L47P, Sch name = HSYNC +# Net "o_vga_vsync" LOC = P7 | IOSTANDARD = LVCMOS33; # Bank = 2, pin name = IO_L47N, Sch name = VSYNC +# +# Time output +##JA +#Net "gpt0_q" LOC = T12 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L19P, Sch name = JA1 +#Net "gpt0_nq" LOC = V12 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L19N, Sch name = JA2 + +## Pic USB-HID interface +# NET "ps2_keyboard_data" LOC = "J13" | IOSTANDARD = "LVCMOS33" | PULLUP; #Bank = 1, Pin name = IO_L39P_M1A3, Sch name = PIC-SDI1 +# NET "ps2_keyboard_clk" LOC = "L12" | IOSTANDARD = "LVCMOS33" | PULLUP; #Bank = 1, Pin name = IO_L40P_GCLK11_M1A5, Sch name = PIC-SCK1 +# +#NET "ps2_mouse_data" LOC = "K14" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L39N_M1ODT, Sch name = PIC-SDO1 +#NET "ps2_mouse_clk" LOC = "L13" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L40N_GCLK10_M1A6, Sch name = PIC-SS1 + +#NET "pic_gpio<0>" LOC = "L16" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L42N_GCLK6_TRDY1_M1LDM, Sch name = PIC-GPIO0 +#NET "pic_gpio<1>" LOC = "H17" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L43P_GCLK5_M1DQ4, Sch name = PIC-GPIO1 + +## 12 pin connectors + +##JA +#Net "JA<0>" LOC = T12 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L19P, Sch name = JA1 +#Net "JA<1>" LOC = V12 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L19N, Sch name = JA2 +#Net "JA<2>" LOC = N10 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L20P, Sch name = JA3 +#Net "JA<3>" LOC = P11 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L20N, Sch name = JA4 +#Net "JA<4>" LOC = M10 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L22P, Sch name = JA7 +#Net "JA<5>" LOC = N9 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L22N, Sch name = JA8 +#Net "JA<6>" LOC = U11 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L23P, Sch name = JA9 +#Net "JA<7>" LOC = V11 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L23N, Sch name = JA10 + +##JB +#Net "JB<0>" LOC = K2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L38P_M3DQ2, Sch name = JB1 +#Net "JB<1>" LOC = K1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L38N_M3DQ3, Sch name = JB2 +#Net "JB<2>" LOC = L4 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L39P_M3LDQS, Sch name = JB3 +#Net "JB<3>" LOC = L3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L39N_M3LDQSN, Sch name = JB4 +#Net "JB<4>" LOC = J3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L40P_M3DQ6, Sch name = JB7 +#Net "JB<5>" LOC = J1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L40N_M3DQ7, Sch name = JB8 +#Net "JB<6>" LOC = K3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L42N_GCLK24_M3LDM, Sch name = JB9 +#Net "JB<7>" LOC = K5 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L43N_GCLK22_IRDY2_M3CASN, Sch name = JB10 + +##JC +#Net "JC<0>" LOC = H3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L44N_GCLK20_M3A6, Sch name = JC1 +#Net "JC<1>" LOC = L7 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L45P_M3A3, Sch name = JC2 +#Net "JC<2>" LOC = K6 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L45N_M3ODT, Sch name = JC3 +#Net "JC<3>" LOC = G3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L46P_M3CLK, Sch name = JC4 +#Net "JC<4>" LOC = G1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L46N_M3CLKN, Sch name = JC7 +#Net "JC<5>" LOC = J7 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L47P_M3A0, Sch name = JC8 +#Net "JC<6>" LOC = J6 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L47N_M3A1, Sch name = JC9 +#Net "JC<7>" LOC = F2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L48P_M3BA0, Sch name = JC10 + +##JD, LX16 Die only +#Net "JD<0>" LOC = G11 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L40P, Sch name = JD1 +#Net "JD<1>" LOC = F10 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L40N, Sch name = JD2 +#Net "JD<2>" LOC = F11 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L42P, Sch name = JD3 +#Net "JD<3>" LOC = E11 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L42N, Sch name = JD4 +#Net "JD<4>" LOC = D12 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L47P, Sch name = JD7 +#Net "JD<5>" LOC = C12 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L47N, Sch name = JD8 +#Net "JD<6>" LOC = F12 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L51P, Sch name = JD9 +#Net "JD<7>" LOC = E12 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L51N, Sch name = JD10 + +## onBoard USB controller +#Net "EppAstb" LOC = H1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L41N_GCLK26_M3DQ5, Sch name = U-FLAGA +#Net "EppDstb" LOC = K4 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L42P_GCLK25_TRDY2_M3UDM, Sch name = U-FLAGB +#Net "EppWait" LOC = C2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L83P, Sch name = U-SLRD +#Net "EppDB<0>" LOC = E1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L50N_M3BA2, Sch name = U-FD0 +#Net "EppDB<1>" LOC = F4 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L51P_M3A10, Sch name = U-FD1 +#Net "EppDB<2>" LOC = F3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L51N_M3A4, Sch name = U-FD2 +#Net "EppDB<3>" LOC = D2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L52P_M3A8, Sch name = U-FD3 +#Net "EppDB<4>" LOC = D1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L52N_M3A9, Sch name = U-FD4 +#Net "EppDB<5>" LOC = H7 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L53P_M3CKE, Sch name = U-FD5 +#Net "EppDB<6>" LOC = G6 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L53N_M3A12, Sch name = U-FD6 +#Net "EppDB<7>" LOC = E4 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L54P_M3RESET, Sch name = U-FD7 + +#Net "UsbClk" LOC = H2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L41P_GCLK27_M3DQ4, Sch name = U-IFCLK +#Net "UsbDir" LOC = F6 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L55P_M3A13, Sch name = U-SLCS + +#Net "UsbWR" LOC = C1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L83N_VREF, Sch name = U-SLWR +#Net "UsbOE" LOC = H6 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L49P_M3A7, Sch name = U-SLOE + +#Net "UsbAdr<1>" LOC = E3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L50P_M3WE, Sch name = U-FIFOAD1 +#Net "UsbAdr<0>" LOC = H5 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L49N_M3A2, Sch name = U-FIFOAD0 + +#Net "UsbPktend" LOC = D3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L54N_M3A11, Sch name = U-PKTEND + +#Net "UsbFlag" LOC = F5 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L55N_M3A14, Sch name = U-FLAGC +#Net "UsbMode" LOC = F1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L48N_M3BA1, Sch name = U-INT0# + +# ## onBoard Cellular RAM, Numonyx StrataFlash and Numonyx Quad Flash +# Net "mem_oe" LOC = L18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L46N_FOE_B_M1DQ3, Sch name = P30-OE +# Net "mem_wr" LOC = M16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L47P_FWE_B_M1DQ0, Sch name = P30-WE +# Net "mem_adv" LOC = H18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L43N_GCLK4_M1DQ5, Sch name = P30-ADV +# Net "mem_wait" LOC = V4 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L63N, Sch name = P30-WAIT +# #Net "MemClk" LOC = R10 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L29P_GCLK3, Sch name = P30-CLK +# +# Net "ram_cs" LOC = L15 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L42P_GCLK7_M1UDM, Sch name = MT-CE +# #Net "RamCRE" LOC = M18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L47N_LDC_M1DQ1, Sch name = MT-CRE +# #Net "RamUB" LOC = K15 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L41P_GCLK9_IRDY1_M1RASN, Sch name = MT-UB +# #Net "RamLB" LOC = K16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L41N_GCLK8_M1CASN, Sch name = MT-LB +# +# Net "flash_cs" LOC = L17 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L46P_FCS_B_M1DQ2, Sch name = P30-CE +# Net "flash_rp" LOC = T4 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L63P, Sch name = P30-RST +# +# #Net "QuadSpiFlashCS" LOC = V3 | IOSTANDARD = LVCMOS33; #Bank = MISC, pin name = IO_L65N_CSO_B_2, Sch name = CS +# #Net "QuadSpiFlashSck" LOC = R15 | IOSTANDARD = LVCMOS33; #Bank = MISC, pin name = IO_L1P_CCLK_2, Sch name = SCK +# +# Net "mem_addr<1>" LOC = K18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L45N_A0_M1LDQSN, Sch name = P30-A0 +# Net "mem_addr<2>" LOC = K17 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L45P_A1_M1LDQS, Sch name = P30-A1 +# Net "mem_addr<3>" LOC = J18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L44N_A2_M1DQ7, Sch name = P30-A2 +# Net "mem_addr<4>" LOC = J16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L44P_A3_M1DQ6, Sch name = P30-A3 +# Net "mem_addr<5>" LOC = G18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L38N_A4_M1CLKN, Sch name = P30-A4 +# Net "mem_addr<6>" LOC = G16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L38P_A5_M1CLK, Sch name = P30-A5 +# Net "mem_addr<7>" LOC = H16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L37N_A6_M1A1, Sch name = P30-A6 +# Net "mem_addr<8>" LOC = H15 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L37P_A7_M1A0, Sch name = P30-A7 +# Net "mem_addr<9>" LOC = H14 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L36N_A8_M1BA1, Sch name = P30-A8 +# Net "mem_addr<10>" LOC = H13 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L36P_A9_M1BA0, Sch name = P30-A9 +# Net "mem_addr<11>" LOC = F18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L35N_A10_M1A2, Sch name = P30-A10 +# Net "mem_addr<12>" LOC = F17 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L35P_A11_M1A7, Sch name = P30-A11 +# Net "mem_addr<13>" LOC = K13 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L34N_A12_M1BA2, Sch name = P30-A12 +# Net "mem_addr<14>" LOC = K12 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L34P_A13_M1WE, Sch name = P30-A13 +# Net "mem_addr<15>" LOC = E18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L33N_A14_M1A4, Sch name = P30-A14 +# Net "mem_addr<16>" LOC = E16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L33P_A15_M1A10, Sch name = P30-A15 +# Net "mem_addr<17>" LOC = G13 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L32N_A16_M1A9, Sch name = P30-A16 +# Net "mem_addr<18>" LOC = H12 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L32P_A17_M1A8, Sch name = P30-A17 +# Net "mem_addr<19>" LOC = D18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L31N_A18_M1A12, Sch name = P30-A18 +# Net "mem_addr<20>" LOC = D17 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L31P_A19_M1CKE, Sch name = P30-A19 +# Net "mem_addr<21>" LOC = G14 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L30N_A20_M1A11, Sch name = P30-A20 +# Net "mem_addr<22>" LOC = F14 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L30P_A21_M1RESET Sch name = P30-A21 +# Net "mem_addr<23>" LOC = C18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L29N_A22_M1A14, Sch name = P30-A22 +# Net "mem_addr<24>" LOC = C17 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L29P_A23_M1A13, Sch name = P30-A23 +# Net "mem_addr<25>" LOC = F16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L1N_A24_VREF, Sch name = P30-A24 +# Net "mem_addr<26>" LOC = F15 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L1P_A25, Sch name = P30-A25 +# +# #Net "QuadSpiFlashDB<0>" LOC = T13 | IOSTANDARD = LVCMOS33; #Dual/Quad SPI Flash DB<0>, Bank = MISC, pin name = IO_L3N_MOSI_CSI_B_MISO0_2, Sch name = SDI +# Net "mem_data<0>" LOC = R13 | IOSTANDARD = LVCMOS33; #Ram or Numonyx Paralell Flash DB<0>, or Dual/Quad SPI Flash DB<1>, Bank = MISC, pin name = IO_L3P_D0_DIN_MISO_MISO1_2, Sch name = P30-DQ0 +# Net "mem_data<1>" LOC = T14 | IOSTANDARD = LVCMOS33; #Ram or Numonyx Paralell Flash DB<1>, or Quad SPI Flash DB<2>, Bank = MISC, pin name = IO_L12P_D1_MISO2_2, Sch name = P30-DQ1 +# Net "mem_data<2>" LOC = V14 | IOSTANDARD = LVCMOS33; #Ram or Numonyx Paralell Flash DB<2>, or Quad SPI Flash DB<3>, Bank = MISC, pin name = IO_L12N_D2_MISO3_2, Sch name = P30-DQ2 +# Net "mem_data<3>" LOC = U5 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_49P_D3, Sch name = P30-DQ3 +# Net "mem_data<4>" LOC = V5 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_49N_D4, Sch name = P30-DQ4 +# Net "mem_data<5>" LOC = R3 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L62P_D5, Sch name = P30-DQ5 +# Net "mem_data<6>" LOC = T3 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L62N_D6, Sch name = P30-DQ6 +# Net "mem_data<7>" LOC = R5 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L48P_D7, Sch name = P30-DQ7 +# Net "mem_data<8>" LOC = N5 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L64P_D8, Sch name = P30-DQ8 +# Net "mem_data<9>" LOC = P6 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L64N_D9, Sch name = P30-DQ9 +# Net "mem_data<10>" LOC = P12 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L13N_D10, Sch name = P30-DQ10 +# Net "mem_data<11>" LOC = U13 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L14P_D11, Sch name = P30-DQ11 +# Net "mem_data<12>" LOC = V13 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L14N_D12, Sch name = P30-DQ12 +# Net "mem_data<13>" LOC = U10 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L30P_GCLK1_D13, Sch name = P30-DQ13 +# Net "mem_data<14>" LOC = R8 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L31P_GCLK31_D14, Sch name = P30-DQ14 +# Net "mem_data<15>" LOC = T8 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L31N_GCLK30_D15, Sch name = P30-DQ15 +# +## SMSC ethernet PHY +#Net "PhyRstn" LOC = P3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L2N, Sch name = ETH-RST +#Net "PhyCrs" LOC = N3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L1N_VREF, Sch name = ETH-CRS +#Net "PhyCol" LOC = P4 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L2P, Sch name = ETH-COL +#Net "PhyClk25Mhz" LOC = N4 | IOSTANDARD = LVCMOS33; #Unconnected if R172 is not loaded, Bank = 3, pin name = IO_L1P, Sch name = ETH-CLK25MHZ + +#Net "PhyTxd<3>" LOC = T1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L33N_M3DQ13, Sch name = ETH-TXD3 +#Net "PhyTxd<2>" LOC = T2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L33P_M3DQ12, Sch name = ETH-TXD2 +#Net "PhyTxd<1>" LOC = U1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L32N_M3DQ15, Sch name = ETH-TXD1 +#Net "PhyTxd<0>" LOC = U2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L32P_M3DQ14, Sch name = ETH-TXD0 +#Net "PhyTxEn" LOC = L2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L37P_M3DQ0, Sch name = ETH-TX_EN +#Net "PhyTxClk" LOC = L5 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L43P_GCLK23_M3RASN, Sch name = ETH-TX_CLK +#Net "PhyTxEr" LOC = P2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L34P_M3UDQS, Sch name = ETH-TXD4 + +#Net "PhyRxd<3>" LOC = M3 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L36P_M3DQ8, Sch name = ETH-RXD3 +#Net "PhyRxd<2>" LOC = N1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L35N_M3DQ11, Sch name = ETH-RXD2 +#Net "PhyRxd<1>" LOC = N2 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L35P_M3DQ10, Sch name = ETH-RXD1 +#Net "PhyRxd<0>" LOC = P1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L34N_M3UDQSN, Sch name = ETH-RXD0 +#Net "PhyRxDv" LOC = L1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L37N_M3DQ1, Sch name = ETH-RX_DV +#Net "PhyRxEr" LOC = M1 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L36N_M3DQ9, Sch name = ETH-RXD4 +#Net "PhyRxClk" LOC = H4 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L44P_GCLK21_M3A5, Sch name = ETH-RX_CLK + +#Net "PhyMdc" LOC = M5 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L31N_VREF, Sch name = ETH-MDC +#Net "PhyMdio" LOC = L6 | IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L31P, Sch name = ETH-MDIO + +## VHDCI Connector +#Net "EXP-IO_P<0>" LOC = B2 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L2P, Sch name = EXP_IO1_P +#Net "EXP-IO_N<0>" LOC = A2 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L2N, Sch name = EXP_IO1_N +#Net "EXP-IO_P<1>" LOC = D6 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L3P, Sch name = EXP_IO2_P +#Net "EXP-IO_N<1>" LOC = C6 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L3N, Sch name = EXP_IO2_N +#Net "EXP-IO_P<2>" LOC = B3 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L4P, Sch name = EXP_IO3_P +#Net "EXP-IO_N<2>" LOC = A3 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L4N, Sch name = EXP_IO3_N +#Net "EXP-IO_P<3>" LOC = B4 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L5P, Sch name = EXP_IO4_P +#Net "EXP-IO_N<3>" LOC = A4 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L5N, Sch name = EXP_IO4_N +#Net "EXP-IO_P<4>" LOC = C5 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L6P, Sch name = EXP_IO5_P +#Net "EXP-IO_N<4>" LOC = A5 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L6N, Sch name = EXP_IO5_N +#Net "EXP-IO_P<5>" LOC = B6 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L8P, Sch name = EXP_IO6_P +#Net "EXP-IO_N<5>" LOC = A6 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L8N_VREF, Sch name = EXP_IO6_N +#Net "EXP-IO_P<6>" LOC = C7 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L10P, Sch name = EXP_IO7_P +#Net "EXP-IO_N<6>" LOC = A7 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L10N, Sch name = EXP_IO7_N +#Net "EXP-IO_P<7>" LOC = D8 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L11P, Sch name = EXP_IO8_P +#Net "EXP-IO_N<7>" LOC = C8 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L11N, Sch name = EXP_IO8_N +#Net "EXP-IO_P<8>" LOC = B9 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L35P_GCLK17, Sch name = EXP_IO9_P +#Net "EXP-IO_N<8>" LOC = A9 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L35N_GCLK16, Sch name = EXP_IO9_N +#Net "EXP-IO_P<9>" LOC = D11 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L36P_GCLK15, Sch name = EXP_IO10_P +#Net "EXP-IO_N<9>" LOC = C11 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L36N_GCLK14, Sch name = EXP_IO10_N +#Net "EXP-IO_P<10>" LOC = C10 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L37P_GCLK13, Sch name = EXP_IO11_P +#Net "EXP-IO_N<10>" LOC = A10 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L37N_GCLK12, Sch name = EXP_IO11_N +#Net "EXP-IO_P<11>" LOC = G9 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L38P, Sch name = EXP_IO12_P +#Net "EXP-IO_N<11>" LOC = F9 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L38N_VREF, Sch name = EXP_IO12_N +#Net "EXP-IO_P<12>" LOC = B11 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L39P, Sch name = EXP_IO13_P +#Net "EXP-IO_N<12>" LOC = A11 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L39N, Sch name = EXP_IO13_N +#Net "EXP-IO_P<13>" LOC = B12 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L41P, Sch name = EXP_IO14_P +#Net "EXP-IO_N<13>" LOC = A12 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L41N, Sch name = EXP_IO14_N +#Net "EXP-IO_P<14>" LOC = C13 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L50P, Sch name = EXP_IO15_P +#Net "EXP-IO_N<14>" LOC = A13 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L50N, Sch name = EXP_IO15_N +#Net "EXP-IO_P<15>" LOC = B14 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L62P, Sch name = EXP_IO16_P +#Net "EXP-IO_N<15>" LOC = A14 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L62N_VREF, Sch name = EXP_IO16_N +#Net "EXP-IO_P<16>" LOC = F13 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L63P_SCP7, Sch name = EXP_IO17_P +#Net "EXP-IO_N<16>" LOC = E13 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L63N_SCP6, Sch name = EXP_IO17_N +#Net "EXP-IO_P<17>" LOC = C15 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L64P_SCP5, Sch name = EXP_IO18_P +#Net "EXP-IO_N<17>" LOC = A15 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L64N_SCP4, Sch name = EXP_IO18_N +#Net "EXP-IO_P<18>" LOC = D14 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L65P_SCP3, Sch name = EXP_IO19_P +#Net "EXP-IO_N<18>" LOC = C14 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L65N_SCP2, Sch name = EXP_IO19_N +#Net "EXP-IO_P<19>" LOC = B16 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L66P_SCP1, Sch name = EXP_IO20_P +#Net "EXP-IO_N<19>" LOC = A16 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L66N_SCP0, Sch name = EXP_IO20_N + Index: bitserial/trunk/top.vhd =================================================================== --- bitserial/trunk/top.vhd (nonexistent) +++ bitserial/trunk/top.vhd (revision 2) @@ -0,0 +1,69 @@ +-- File: top.vhd +-- Author: Richard James Howe +-- Repository: https://github.com/howerj/bit-serial +-- License: MIT +-- Description: Top level entity; Bit Serial CPU + +library ieee, work, std; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.util.all; + +entity top is + generic ( + g: common_generics := default_settings; + file_name: string := "bit.hex"; + N: positive := 16; + baud: positive := 115200; + debug: natural := 0; -- will not synthesize if greater than zero (debug off) + uart_use_cfg: boolean := false; + uart_fifo_depth: natural := 0 + ); + port ( + clk: in std_ulogic; + -- synthesis translate_off +-- rst: in std_ulogic; + halt: out std_ulogic; + -- synthesis translate_on + tx: out std_ulogic; + rx: in std_ulogic; + sw: in std_ulogic_vector(7 downto 0); + ld: out std_ulogic_vector(7 downto 0)); +end entity; + +architecture rtl of top is + constant W: positive := N - 3; + signal rst: std_ulogic := '0'; + signal i: std_ulogic := 'X'; + signal o, a, oe, ie, ae: std_ulogic := 'X'; +begin + peripheral: entity work.peripherals + generic map( + g => g, + file_name => file_name, + W => W, + N => N, + baud => baud, + uart_fifo_depth => uart_fifo_depth, + uart_use_cfg => uart_use_cfg) + port map ( + clk => clk, rst => rst, + tx => tx, rx => rx, ld => ld, sw => sw, + i => o, + o => i, + a => a, oe => ie, ie => oe, ae => ae); + + cpu: entity work.bcpu + generic map ( + asynchronous_reset => g.asynchronous_reset, + delay => g.delay, + N => N, + debug => debug) + port map ( + clk => clk, rst => rst, + -- synthesis translate_off + stop => halt, + -- synthesis translate_on + i => i, + o => o, a => a, oe => oe, ie => ie, ae => ae); +end architecture; Index: bitserial/trunk/uart.vhd =================================================================== --- bitserial/trunk/uart.vhd (nonexistent) +++ bitserial/trunk/uart.vhd (revision 2) @@ -0,0 +1,1050 @@ +-- FILE: uart.vhd +-- BRIEF: UART TX/RX module +-- LICENSE: MIT +-- COPYRIGHT: Richard James Howe (2019, 2020) +-- +-- The UART (Universal Asynchronous Receiver/Transmitter) is one of the simplest +-- serial communication methods available. It is often used for debugging, for +-- issuing commands to embedded devices and sometimes even for uploading firmware +-- to devices. The data format and speed are configurable but there is no method +-- for automatically configuring a UART, both sides must have agreed on the +-- settings before hand. Configurable options include; baud, use of an +-- even of odd parity bit, number of data bits and number of stop bits. +-- +-- The clock is not transmitted as part of the signal (which is why baud +-- must be agreed upon before hand), a single packet starts with a 'start bit', +-- where the line goes low. The receiver must synchronize to this start bit (it +-- resets the clock that generates pulse at the sample rate and baud when it +-- encounters a start bit). +-- +-- A transmission with 8 data bits, 1 parity bit and 1 stop bit looks like +-- this: +-- ____ ______________________________ ________________ +-- \_/|0|1|2|3|4|5|6|7|P|S| \_/ +-- Start Data Parity Stop |--- More data ---> +-- +-- Start bits are always low, stop bits high. The most common format is +-- 8 data bits, no parity bit, and 1 stop bit at either 9600 or 115200 baud. +-- +-- For the receiver a clock that is a multiple of the baud is used so the +-- bits can be sampled with a higher frequency than the bit rate. +-- +-- Some notes: +-- * We can transmit from an 8-bit UART to a less than 8-bit UART fine, so +-- long as parity is not used, as +-- * Certain UARTs have the ability to transmit a BREAK signal by holding +-- the line low for a period greater than the packet length. We can transmit +-- that by lowering the baud rate and transmitting all zeroes. Receiving a +-- break (correctly) would require changing the receiver. +-- +-- +-- See: +-- * +-- +-- NOTE: We could replace this entire package with an entirely software driven +-- solution. The only hardware we would need two timers driven at the sample +-- rate (one for RX, one for TX) and a deglitched RX signal. An interrupt +-- would be generated on the timers expiry. +-- +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package uart_pkg is + constant uart_8N1: std_ulogic_vector(7 downto 0) := "10000100"; + + type uart_generics is record + clock_frequency: positive; -- clock frequency of module clock + delay: time; -- gate delay for simulation purposes + asynchronous_reset: boolean; -- use asynchronous reset if true + end record; + + component uart_tx is + generic ( + g: uart_generics; + N: positive; + format: std_ulogic_vector(7 downto 0) := uart_8N1; + use_cfg: boolean); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + cr: out std_ulogic; + baud: in std_ulogic; -- Pulse at baud + tx: out std_ulogic; + ok: out std_ulogic; + ctr: in std_ulogic_vector(format'range); + ctr_we: in std_ulogic; + we: in std_ulogic; -- di write enable + di: in std_ulogic_vector(N - 1 downto 0)); + end component; + + component uart_rx is + generic ( + g: uart_generics; + N: positive; + D: positive; + format: std_ulogic_vector(7 downto 0) := uart_8N1; + use_cfg: boolean); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + cr: out std_ulogic; + baud: in std_ulogic; + sample: in std_ulogic; + failed: out std_ulogic_vector(1 downto 0); + ctr: in std_ulogic_vector(7 downto 0); + ctr_we: in std_ulogic; + rx: in std_ulogic; + we: out std_ulogic; + do: out std_ulogic_vector(N - 1 downto 0)); + end component; + + component uart_fifo is + generic ( + g: uart_generics; + data_width: positive; + fifo_depth: positive; + read_first: boolean := true); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + di: in std_ulogic_vector(data_width - 1 downto 0); + we: in std_ulogic; + re: in std_ulogic; + do: out std_ulogic_vector(data_width - 1 downto 0); + + -- optional + full: out std_ulogic := '0'; + empty: out std_ulogic := '1'); + end component; + + component uart_baud is -- Generates a pulse at the sample rate and baud + generic ( + g: uart_generics; + init: integer; + N: positive := 16; + D: positive := 3); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + we: in std_ulogic; + cnt: in std_ulogic_vector(N - 1 downto 0); + cr: in std_ulogic := '0'; + sample: out std_ulogic; + baud: out std_ulogic); + end component; + + component uart_top is + generic ( + clock_frequency: positive; -- clock frequency of module clock + delay: time; -- gate delay for simulation purposes + asynchronous_reset: boolean; -- use asynchronous reset if true + baud: positive := 115200; + format: std_ulogic_vector(7 downto 0) := uart_8N1; + fifo_depth: natural := 0; + use_cfg: boolean := false; + use_tx: boolean := true; + use_rx: boolean := true + ); -- Use FIFO to buffer results? + port ( + clk: in std_ulogic; + rst: in std_ulogic; + + tx: out std_ulogic; + tx_fifo_full: out std_ulogic; + tx_fifo_empty: out std_ulogic; + tx_fifo_we: in std_ulogic; + tx_fifo_data: in std_ulogic_vector(7 downto 0); + + rx: in std_ulogic; + rx_fifo_full: out std_ulogic; + rx_fifo_empty: out std_ulogic; + rx_fifo_re: in std_ulogic; + rx_fifo_data: out std_ulogic_vector(7 downto 0); + + reg: in std_ulogic_vector(15 downto 0); + clock_reg_tx_we: in std_ulogic; + clock_reg_rx_we: in std_ulogic; + control_reg_we: in std_ulogic); + end component; + + component uart_tb is + generic(g: uart_generics); + end component; + + function parity(slv:std_ulogic_vector; even: boolean) return std_ulogic; + function parity(slv:std_ulogic_vector; even: std_ulogic) return std_ulogic; + constant ctr_use_parity: integer := 0; + constant ctr_even_parity: integer := 1; + function ctr_stop_bits(ctr: std_ulogic_vector(7 downto 0)) return integer; + function ctr_data_bits(ctr: std_ulogic_vector(7 downto 0)) return integer; +end package; + +package body uart_pkg is + function parity(slv: std_ulogic_vector; even: boolean) return std_ulogic is + variable z: std_ulogic := '0'; + begin + if not even then + z := '1'; + end if; + for i in slv'range loop + z := z xor slv(i); + end loop; + return z; + end; + + function parity(slv:std_ulogic_vector; even: std_ulogic) return std_ulogic is + variable z: boolean := false; + begin + if even = '1' then + z := true; + end if; + return parity(slv, z); + end; + + function ctr_stop_bits(ctr: std_ulogic_vector(7 downto 0)) return integer is + variable ii: std_ulogic_vector(1 downto 0); + begin + ii := ctr(3 downto 2); + return to_integer(unsigned(ii)); + end function; + + function ctr_data_bits(ctr: std_ulogic_vector(7 downto 0)) return integer is + variable ii: std_ulogic_vector(3 downto 0); + begin + ii := ctr(7 downto 4); + return to_integer(unsigned(ii)); + end function; +end; + +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.uart_pkg.all; + +entity uart_top is + generic ( + clock_frequency: positive; -- clock frequency of module clock + delay: time; -- gate delay for simulation purposes + asynchronous_reset: boolean; -- use asynchronous reset if true + baud: positive := 115200; + format: std_ulogic_vector(7 downto 0) := uart_8N1; + fifo_depth: natural := 0; -- FIFO depth, 0,1 = no FIFO + use_cfg: boolean := false; -- allow run time configuration of baud rate and format? + use_tx: boolean := true; -- instantiate TX functionality? + use_rx: boolean := true -- instantiate RX functionality? + ); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + + tx: out std_ulogic; + tx_fifo_full: out std_ulogic; + tx_fifo_empty: out std_ulogic; + tx_fifo_we: in std_ulogic; + tx_fifo_data: in std_ulogic_vector(7 downto 0); + + rx: in std_ulogic; + rx_fifo_full: out std_ulogic; + rx_fifo_empty: out std_ulogic; + rx_fifo_re: in std_ulogic; + rx_fifo_data: out std_ulogic_vector(7 downto 0); + + reg: in std_ulogic_vector(15 downto 0); + clock_reg_tx_we: in std_ulogic; + clock_reg_rx_we: in std_ulogic; + control_reg_we: in std_ulogic); +end entity; + +architecture structural of uart_top is + constant g: uart_generics := ( + clock_frequency => clock_frequency, + delay => delay, + asynchronous_reset => asynchronous_reset + ); + constant tx_init: integer := g.clock_frequency / (baud * 16); -- 54 = 115200 @ 100 MHz + constant rx_init: positive := tx_init - 1; -- 50 = 115200 @ 100 MHz + Fudge Factor + constant N: positive := 8; + + signal rx_ok, rx_nd, rx_push, rx_re: std_ulogic := '0'; + signal rx_pushed: std_ulogic_vector(rx_fifo_data'range) := (others => '0'); + signal tx_pop, tx_ok: std_ulogic := '0'; + signal tx_popped: std_ulogic_vector(tx_fifo_data'range) := (others => '0'); + signal tx_fifo_empty_b, rx_fifo_full_b: std_ulogic := '0'; + + signal tx_sample, tx_baud, tx_cr: std_ulogic := '0'; + signal rx_sample, rx_baud, rx_cr, rx_we: std_ulogic := '0'; + signal rx_fail: std_ulogic_vector(1 downto 0) := (others => '0'); + signal rx_ok_buf: std_ulogic := '0'; + signal do, do_c, do_n: std_ulogic_vector(rx_fifo_data'range) := (others => '0'); + signal fail_c, fail_n: std_ulogic_vector(1 downto 0) := (others => '0'); + signal nd_c, nd_n: std_ulogic := '0'; -- new data + +begin + rx_ok_buf <= not (fail_c(0) or fail_c(1)) after g.delay; + rx_ok <= rx_ok_buf; + rx_pushed <= do after g.delay; + rx_nd <= nd_c and rx_ok_buf after g.delay; -- no new data if there are errors + rx_re <= rx_push; + + process (clk, rst) + begin + if use_rx then + if rst = '1' and g.asynchronous_reset then + do_c <= (others => '0') after g.delay; + fail_c <= (others => '0') after g.delay; + nd_c <= '0' after g.delay; + elsif rising_edge(clk) then + if rst = '1' and not g.asynchronous_reset then + do_c <= (others => '0') after g.delay; + fail_c <= (others => '0') after g.delay; + nd_c <= '0' after g.delay; + else + do_c <= do_n after g.delay; + nd_c <= nd_n after g.delay; + fail_c <= fail_n after g.delay; + end if; + end if; + end if; + end process; + + process (do_c, do, nd_c, rx_we, rx_re, fail_c, rx_fail) + begin + if use_tx then + do_n <= do_c after g.delay; + nd_n <= nd_c after g.delay; + fail_n <= fail_c after g.delay; + if rx_we = '1' then + nd_n <= '1' after g.delay; + do_n <= do after g.delay; + fail_n <= rx_fail after g.delay; + elsif rx_re = '1' then + nd_n <= '0' after g.delay; + end if; + end if; + end process; + + ugen0: if fifo_depth <= 1 generate + tx_pop <= tx_fifo_we; + tx_popped <= tx_fifo_data; + tx_fifo_full <= not tx_ok; + tx_fifo_empty <= tx_ok; + + rx_push <= rx_fifo_re; + rx_fifo_data <= rx_pushed; + rx_fifo_full <= rx_nd; + rx_fifo_empty <= not rx_nd; + end generate; + + ugen1: if fifo_depth > 1 generate + tx_fifo_empty <= tx_fifo_empty_b; + tx_pop <= tx_ok and not tx_fifo_empty_b; + + ugen1_fifo_tx: if use_tx generate + uart_fifo_tx_0: work.uart_pkg.uart_fifo + generic map(g => g, + data_width => tx_fifo_data'length, + fifo_depth => fifo_depth) + port map (clk => clk, rst => rst, + di => tx_fifo_data, + we => tx_fifo_we, + re => tx_pop, + do => tx_popped, + full => tx_fifo_full, + empty => tx_fifo_empty_b); + end generate; + + rx_fifo_full <= rx_fifo_full_b; + rx_push <= rx_nd and rx_ok and not rx_fifo_full_b; + ugen1_fifo_rx: if use_rx generate + uart_fifo_rx_0: work.uart_pkg.uart_fifo + generic map(g => g, + data_width => rx_fifo_data'length, + fifo_depth => fifo_depth, + read_first => false) + port map (clk => clk, rst => rst, + di => rx_pushed, + we => rx_push, + re => rx_fifo_re, + do => rx_fifo_data, + full => rx_fifo_full_b, empty => rx_fifo_empty); + end generate; + end generate; + + uart_tx_gen: if use_tx generate + baud_tx: work.uart_pkg.uart_baud + generic map(g => g, init => tx_init, N => reg'length, D => 3) + port map( + clk => clk, + rst => rst, + we => clock_reg_tx_we, + cnt => reg, -- 0x32/50 is 1152000 @ 100MHz clk + cr => tx_cr, + sample => tx_sample, + baud => tx_baud); + + tx_0: work.uart_pkg.uart_tx + generic map(g => g, N => N, format => format, use_cfg => use_cfg) + port map( + clk => clk, + rst => rst, + + cr => tx_cr, + baud => tx_baud, + ok => tx_ok, + we => tx_pop, + ctr => reg(reg'high downto reg'high - 7), + ctr_we => control_reg_we, + di => tx_popped, + tx => tx); + end generate; + + uart_rx_gen: if use_rx generate + baud_rx: work.uart_pkg.uart_baud + generic map(g => g, init => rx_init, N => reg'length, D => 3) + port map( + clk => clk, + rst => rst, + we => clock_reg_rx_we, + cnt => reg, + cr => rx_cr, + sample => rx_sample, + baud => rx_baud); + + rx_0: work.uart_pkg.uart_rx + generic map(g => g, N => N, D => 3, format => format, use_cfg => use_cfg) + port map( + clk => clk, + rst => rst, + + baud => rx_baud, + sample => rx_sample, + cr => rx_cr, + failed => rx_fail, + ctr => reg(reg'low + 7 downto reg'low), + ctr_we => control_reg_we, + we => rx_we, + do => do, + rx => rx); + end generate; +end architecture; + +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.uart_pkg.all; + +entity uart_fifo is + generic (g: uart_generics; + data_width: positive; + fifo_depth: positive; + read_first: boolean := true); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + di: in std_ulogic_vector(data_width - 1 downto 0); + we: in std_ulogic; + re: in std_ulogic; + do: out std_ulogic_vector(data_width - 1 downto 0); + + -- optional + full: out std_ulogic := '0'; + empty: out std_ulogic := '1'); +end uart_fifo; + +architecture behavior of uart_fifo is + type fifo_data_t is array (0 to fifo_depth - 1) of std_ulogic_vector(di'range); + signal data: fifo_data_t := (others => (others => '0')); + function rindex_init return integer is + begin + if read_first then + return 0; + end if; + return fifo_depth - 1; + end function; + + signal count: integer range 0 to fifo_depth := 0; + signal windex: integer range 0 to fifo_depth - 1 := 0; + signal rindex: integer range 0 to fifo_depth - 1 := rindex_init; + + signal is_full: std_ulogic := '0'; + signal is_empty: std_ulogic := '1'; +begin + do <= data(rindex) after g.delay; + full <= is_full after g.delay; -- buffer these bad boys + empty <= is_empty after g.delay; + is_full <= '1' when count = fifo_depth else '0' after g.delay; + is_empty <= '1' when count = 0 else '0' after g.delay; + + process (rst, clk) is + begin + if rst = '1' and g.asynchronous_reset then + windex <= 0 after g.delay; + count <= 0 after g.delay; + rindex <= rindex_init after g.delay; + elsif rising_edge(clk) then + if rst = '1' and not g.asynchronous_reset then + windex <= 0 after g.delay; + count <= 0 after g.delay; + rindex <= rindex_init after g.delay; + else + if we = '1' and re = '0' then + if is_full = '0' then + count <= count + 1 after g.delay; + end if; + elsif we = '0' and re = '1' then + if is_empty = '0' then + count <= count - 1 after g.delay; + end if; + end if; + + if re = '1' and is_empty = '0' then + if rindex = (fifo_depth - 1) then + rindex <= 0 after g.delay; + else + rindex <= rindex + 1 after g.delay; + end if; + end if; + + if we = '1' and is_full = '0' then + if windex = (fifo_depth - 1) then + windex <= 0 after g.delay; + else + windex <= windex + 1 after g.delay; + end if; + data(windex) <= di after g.delay; + end if; + end if; + end if; + end process; +end behavior; + +-- This module generates a sample pulse and a baud pulse. The sample rate +-- can be controlled by setting 'cnt'. This sample rate is then divided by +-- 2^(D+1) to get the baud. +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.uart_pkg.all; + +entity uart_baud is + generic (g: uart_generics; init: integer; N: positive := 16; D: positive := 3); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + + we: in std_ulogic; + cnt: in std_ulogic_vector(N - 1 downto 0); + cr: in std_ulogic := '0'; + + sample: out std_ulogic; -- sample pulse + baud: out std_ulogic); -- baud (sample rate / 2^(D+1)) +end entity; + +architecture behaviour of uart_baud is + constant cmp_init: std_ulogic_vector := std_ulogic_vector(to_unsigned(init, cnt'length)); + signal cmp_c, cmp_n: std_ulogic_vector(cnt'range) := cmp_init; + signal cnt_c, cnt_n: std_ulogic_vector(cnt'range) := (others => '0'); + signal div_c, div_n: std_ulogic_vector(D downto 0) := (others => '0'); + signal pul_c, pul_n: std_ulogic := '0'; + signal pulse: std_ulogic := '0'; +begin + pulse <= (not cr) and pul_n and (pul_c xor pul_n) after g.delay; -- rising edge detector + baud <= (not cr) and div_n(div_n'high) and (div_c(div_c'high) xor div_n(div_n'high)) after g.delay; + sample <= pulse; + + process (clk, rst) + begin + if rst = '1' and g.asynchronous_reset then + cmp_c <= cmp_init after g.delay; + cnt_c <= (others => '0') after g.delay; + div_c <= (others => '0') after g.delay; + pul_c <= '0' after g.delay; + elsif rising_edge(clk) then + if rst = '1' and not g.asynchronous_reset then + cmp_c <= cmp_init after g.delay; + cnt_c <= (others => '0') after g.delay; + div_c <= (others => '0') after g.delay; + pul_c <= '0' after g.delay; + else + cmp_c <= cmp_n after g.delay; + cnt_c <= cnt_n after g.delay; + div_c <= div_n after g.delay; + pul_c <= pul_n after g.delay; + end if; + end if; + end process; + + process (pulse, div_c, cr, we) + begin + div_n <= div_c after g.delay; + if cr = '1' or we = '1' then + div_n <= (others => '0') after g.delay; + div_n(div_n'high) <= '1' after g.delay; + elsif pulse = '1' then + div_n <= std_ulogic_vector(unsigned(div_c) + 1) after g.delay; + end if; + end process; + + process (cmp_c, cnt_c, we, cnt, cr) + begin + cmp_n <= cmp_c after g.delay; + cnt_n <= cnt_c after g.delay; + + if we = '1' then + cmp_n <= cnt after g.delay; + cnt_n <= (others => '0') after g.delay; + pul_n <= '0' after g.delay; + elsif cr = '1' then + cnt_n <= (others => '0') after g.delay; + pul_n <= '0' after g.delay; + elsif cnt_c = cmp_c then + cnt_n <= (others => '0') after g.delay; + pul_n <= '1' after g.delay; + else + cnt_n <= std_ulogic_vector(unsigned(cnt_c) + 1) after g.delay; + pul_n <= '0' after g.delay; + end if; + end process; +end architecture; + +library ieee, work; +use ieee.std_logic_1164.all; +use work.uart_pkg.all; + +entity uart_rx is + generic ( + g: uart_generics; + N: positive; + D: positive; + format: std_ulogic_vector(7 downto 0) := uart_8N1; + use_cfg: boolean); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + cr: out std_ulogic; -- reset sample/baud clock when start bit detected + baud: in std_ulogic; + sample: in std_ulogic; -- pulses at baud and sample rate + failed: out std_ulogic_vector(1 downto 0); + ctr: in std_ulogic_vector(7 downto 0); + ctr_we: in std_ulogic; + rx: in std_ulogic; -- physical RX signal + we: out std_ulogic; -- write 'do' to output register + do: out std_ulogic_vector(N - 1 downto 0)); +end entity; + +architecture behaviour of uart_rx is + type state is (reset, idle, start, data, parity, stop, done); + signal state_c, state_n: state; + signal ctr_c, ctr_n: std_ulogic_vector(ctr'range); + signal rx_c, rx_n: std_ulogic_vector(do'range); + signal sr_c, sr_n: std_ulogic_vector(D - 1 downto 0); + signal rx_sync: std_ulogic; + signal count_c, count_n: integer range 0 to N - 1; + signal majority: std_ulogic; + signal fail_c, fail_n: std_ulogic_vector(1 downto 0); +begin + do <= rx_c after g.delay; + failed <= fail_c after g.delay; + + assert D < 4 severity failure; + + majority_1: if D = 1 generate + majority <= sr_c(0) after g.delay; + end generate; + + majority_2: if D = 2 generate + majority <= sr_c(0) and sr_c(1) after g.delay; -- even wins is 'sr_c(0) or sr_c(1)' + end generate; + + majority_3: if D = 3 generate + majority <= (sr_c(0) and sr_c(1)) or (sr_c(1) and sr_c(2)) or (sr_c(0) and sr_c(2)) after g.delay; + end generate; + + process (clk, rst) + procedure reset is + begin + rx_c <= (others => '0') after g.delay; + sr_c <= (others => '0') after g.delay; + fail_c <= (others => '0') after g.delay; + state_c <= reset after g.delay; + count_c <= 0 after g.delay; + rx_sync <= '0' after g.delay; + if use_cfg then ctr_c <= (others => '0') after g.delay; end if; + end procedure; + begin + if rst = '1' and g.asynchronous_reset then + reset; + elsif rising_edge(clk) then + if rst = '1' and not g.asynchronous_reset then + reset; + else + rx_c <= rx_n after g.delay; + sr_c <= sr_n after g.delay; + state_c <= state_n after g.delay; + count_c <= count_n after g.delay; + fail_c <= fail_n after g.delay; + rx_sync <= rx after g.delay; + if use_cfg then ctr_c <= ctr_n after g.delay; end if; + end if; + end if; + if not use_cfg then ctr_c <= format after g.delay; end if; + end process; + + process (rx_c, sr_c, ctr_c, ctr_we, ctr, state_c, rx_sync, baud, sample, count_c, fail_c, majority) + begin + fail_n <= fail_c after g.delay; + rx_n <= rx_c after g.delay; + sr_n <= sr_c after g.delay; + state_n <= state_c after g.delay; + we <= '0' after g.delay; + cr <= '0' after g.delay; + count_n <= count_c after g.delay; + if use_cfg then ctr_n <= ctr_c after g.delay; end if; + + if sample = '1' then + sr_n <= sr_c(sr_c'high - 1 downto sr_c'low) & rx_sync after g.delay; + end if; + + case state_c is + when reset => + rx_n <= (others => '0') after g.delay; + sr_n <= (others => '0') after g.delay; + fail_n <= (others => '0') after g.delay; + if use_cfg then + ctr_n <= format after g.delay; -- 8 bits, 1 stop bit, parity off + end if; + state_n <= idle after g.delay; + when idle => + count_n <= 0 after g.delay; + if rx_sync = '0' then -- and majority = '1' then + state_n <= start after g.delay; + cr <= '1' after g.delay; + sr_n <= (others => '0') after g.delay; + fail_n <= (others => '0') after g.delay; + end if; + when start => + if baud = '1' then + if majority /= '0' then + state_n <= done after g.delay; -- frame error + fail_n(0) <= '1' after g.delay; + else + state_n <= data after g.delay; + end if; + sr_n <= (others => '0') after g.delay; + end if; + when data => + rx_n(count_c) <= majority after g.delay; + if baud = '1' then + if count_c = (ctr_data_bits(ctr_c) - 1) then + count_n <= 0 after g.delay; + if ctr_c(ctr_use_parity) = '1' then + state_n <= parity after g.delay; + else + state_n <= stop after g.delay; + end if; + else + count_n <= count_c + 1 after g.delay; + end if; + sr_n <= (others => '0') after g.delay; + end if; + when parity => + if baud = '1' then + if (ctr_c(ctr_use_parity) = '1') and (majority /= parity(rx_c, ctr_c(ctr_even_parity))) then + fail_n(1) <= '1' after g.delay; -- parity error, still process stop bits + end if; + state_n <= stop after g.delay; + sr_n <= (others => '0') after g.delay; + end if; + when stop => + if baud = '1' then + if majority /= '1' then + state_n <= done after g.delay; -- frame error + fail_n(0) <= '1' after g.delay; + elsif count_c = ctr_stop_bits(ctr_c) then + state_n <= done after g.delay; + else + count_n <= count_c + 1 after g.delay; + end if; + end if; + when done => -- The consuming module needs to store rx_c/fail_c immediately + we <= '1' after g.delay; + state_n <= idle after g.delay; + end case; + + if ctr_we = '1' then + if not (state_c = idle or state_c = done or state_c = reset) then + rx_n <= (others => '0') after g.delay; + state_n <= idle after g.delay; + we <= '1' after g.delay; + fail_n(0) <= '1' after g.delay; -- frame error! + end if; + ctr_n <= ctr after g.delay; + end if; + end process; +end architecture; + +library ieee, work; +use ieee.std_logic_1164.all; +use work.uart_pkg.all; + +entity uart_tx is + generic ( + g: uart_generics; + N: positive; + format: std_ulogic_vector(7 downto 0) := uart_8N1; + use_cfg: boolean); + port ( + clk: in std_ulogic; + rst: in std_ulogic; + cr: out std_ulogic; + baud: in std_ulogic; -- Pulse at baud + tx: out std_ulogic; + ok: out std_ulogic; + ctr: in std_ulogic_vector(format'range); + ctr_we: in std_ulogic; + we: in std_ulogic; -- di write enable + di: in std_ulogic_vector(N - 1 downto 0)); +end entity; + +architecture behaviour of uart_tx is + type state is (reset, idle, start, data, parity, stop); + signal state_c, state_n: state; + signal di_c, di_n: std_ulogic_vector(di'range); + signal ctr_c, ctr_n: std_ulogic_vector(ctr'range); + signal busy: std_ulogic; + signal parity_c, parity_n: std_ulogic; + signal count_c, count_n: integer range 0 to 15; +begin + busy <= '0' when state_c = idle else '1' after g.delay; + ok <= not busy after g.delay; + + process (clk, rst) + procedure reset is + begin + di_c <= (others => '0') after g.delay; + state_c <= reset after g.delay; + parity_c <= '0' after g.delay; + count_c <= 0 after g.delay; + if use_cfg then ctr_c <= (others => '0') after g.delay; end if; + end procedure; + begin + if rst = '1' and g.asynchronous_reset then + reset; + elsif rising_edge(clk) then + if rst = '1' and not g.asynchronous_reset then + reset; + else + di_c <= di_n after g.delay; + state_c <= state_n after g.delay; + parity_c <= parity_n after g.delay; + count_c <= count_n after g.delay; + if use_cfg then ctr_c <= ctr_n after g.delay; end if; + end if; + end if; + if not use_cfg then ctr_c <= format after g.delay; end if; + end process; + + process (di_c, di, ctr_c, ctr_we, ctr, state_c, we, baud, parity_c, count_c) + begin + count_n <= count_c after g.delay; + state_n <= state_c after g.delay; + di_n <= di_c after g.delay; + if use_cfg then ctr_n <= ctr_c after g.delay; end if; + tx <= '1' after g.delay; + cr <= '0'; + + if ctr_c(ctr_use_parity) = '1' then + parity_n <= parity(di_c, ctr_c(ctr_even_parity)) after g.delay; + else + parity_n <= '0' after g.delay; + end if; + + case state_c is + when reset => + state_n <= idle after g.delay; + ctr_n <= format after g.delay; -- 8 bits, 1 stop bit, parity off + count_n <= 0 after g.delay; + parity_n <= '0' after g.delay; + di_n <= (others => '0') after g.delay; + when idle => + count_n <= 0 after g.delay; + if use_cfg and ctr_we = '1' then -- NB. We can either lose data, or control writes + ctr_n <= ctr after g.delay; + elsif we = '1' then + di_n <= di after g.delay; + cr <= '1'; + state_n <= start after g.delay; + end if; + when start => + tx <= '0' after g.delay; + if baud = '1' then + state_n <= data after g.delay; + end if; + when data => + tx <= di_c(count_c) after g.delay; + if baud = '1' then + if count_c = (ctr_data_bits(ctr_c) - 1) then + count_n <= 0 after g.delay; + if ctr_c(ctr_use_parity) = '1' then + state_n <= parity after g.delay; + else + state_n <= stop after g.delay; + end if; + else + count_n <= count_c + 1 after g.delay; + end if; + end if; + when parity => + tx <= parity_c after g.delay; + if baud = '1' then + state_n <= stop after g.delay; + end if; + when stop => + tx <= '1' after g.delay; + if baud = '1' then + if count_c = ctr_stop_bits(ctr_c) then + count_n <= 0 after g.delay; + state_n <= idle after g.delay; + else + count_n <= count_c + 1 after g.delay; + end if; + end if; + end case; + end process; +end architecture; + +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.uart_pkg.all; + +entity uart_tb is + generic(g: uart_generics); +end entity; + +architecture testing of uart_tb is + constant clock_period: time := 1000 ms / g.clock_frequency; + constant fifo_depth: natural := 8; + signal rst, clk: std_ulogic := '1'; + signal stop: boolean := false; + signal loopback: boolean := true; + signal tx, rx: std_ulogic; + signal di, do: std_ulogic_vector(7 downto 0); + signal reg: std_ulogic_vector(15 downto 0); + signal clock_reg_tx_we: std_ulogic; + signal clock_reg_rx_we: std_ulogic; + signal control_reg_we: std_ulogic; + + signal tx_fifo_full: std_ulogic; + signal tx_fifo_empty: std_ulogic; + signal tx_fifo_we: std_ulogic := '0'; + signal rx_fifo_full: std_ulogic; + signal rx_fifo_empty: std_ulogic; + signal rx_fifo_re: std_ulogic := '0'; +begin + -- duration: process begin wait for 20000 us; stop <= true; wait; end process; + clk_process: process + begin + rst <= '1'; + wait for clock_period * 5; + rst <= '0'; + while not stop loop + clk <= '1'; + wait for clock_period / 2; + clk <= '0'; + wait for clock_period / 2; + end loop; + wait; + end process; + + stimulus: process + procedure write(data: std_ulogic_vector(di'range)) is + begin + wait for clock_period * 1; + while tx_fifo_full = '1' loop + wait for clock_period; + end loop; + di <= data; + tx_fifo_we <= '1'; + wait for clock_period; + tx_fifo_we <= '0'; + end procedure; + begin + di <= x"00"; + wait until rst = '0'; + wait for clock_period; + reg <= x"8080"; + control_reg_we <= '1'; + wait for clock_period; + control_reg_we <= '0'; + reg <= x"0036"; + clock_reg_tx_we <= '1'; + wait for clock_period; + clock_reg_tx_we <= '0'; + clock_reg_rx_we <= '1'; + reg <= x"0035"; + wait for clock_period; + clock_reg_rx_we <= '0'; + wait for clock_period; + + write(x"AA"); + write(x"BB"); + write(x"CC"); + write(x"DD"); + write(x"EE"); + write(x"FF"); + wait for clock_period; + while tx_fifo_empty = '0' loop + wait for clock_period; + end loop; + loopback <= false; + wait for clock_period * 50000; + stop <= true; + wait; + end process; + + ack: process + begin + while not stop loop + if rx_fifo_empty = '0' then + rx_fifo_re <= '1'; + else + rx_fifo_re <= '0'; + end if; + wait for clock_period; + end loop; + wait; + end process; + rx <= tx when loopback else '0'; -- loop back test + uut: work.uart_pkg.uart_top + generic map ( + clock_frequency => g.clock_frequency, + delay => g.delay, + asynchronous_reset => g.asynchronous_reset, + baud => 115200, + format => uart_8N1, + fifo_depth => fifo_depth) + port map ( + clk => clk, + rst => rst, + + tx => tx, + tx_fifo_full => tx_fifo_full, + tx_fifo_empty => tx_fifo_empty, + tx_fifo_we => tx_fifo_we, + tx_fifo_data => di, + + rx => rx, + rx_fifo_full => rx_fifo_full, + rx_fifo_empty => rx_fifo_empty, + rx_fifo_re => rx_fifo_re, + rx_fifo_data => do, + + reg => reg, + clock_reg_tx_we => clock_reg_tx_we, + clock_reg_rx_we => clock_reg_rx_we, + control_reg_we => control_reg_we); + +end architecture; + Index: bitserial/trunk/util.vhd =================================================================== --- bitserial/trunk/util.vhd (nonexistent) +++ bitserial/trunk/util.vhd (revision 2) @@ -0,0 +1,161 @@ +-- File: util.vhd +-- Author: Richard James Howe +-- Repository: https://github.com/howerj/bit-serial +-- License: MIT +-- Description: Utility module, mostly taken from another project +-- of mine, . + +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; + +package util is + -- Not all modules will need every generic specified here, even so it + -- is easier to group the common generics in one structure. + type common_generics is record + clock_frequency: positive; -- clock frequency of module clock + delay: time; -- gate delay for simulation purposes + asynchronous_reset: boolean; -- use asynchronous reset if true + end record; + + constant default_settings: common_generics := ( + clock_frequency => 100_000_000, + delay => 0 ns, + asynchronous_reset => true + ); + + type file_format is (FILE_HEX, FILE_BINARY, FILE_NONE); + + component single_port_block_ram is + generic (g: common_generics; + addr_length: positive := 12; + data_length: positive := 16; + file_name: string := "memory.bin"; + file_type: file_format := FILE_BINARY); + port ( + clk: in std_ulogic; + dwe: in std_ulogic; + dre: in std_ulogic; + addr: in std_ulogic_vector(addr_length - 1 downto 0); + din: in std_ulogic_vector(data_length - 1 downto 0); + dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0')); + end component; + + function hex_char_to_std_ulogic_vector_tb(hc: character) return std_ulogic_vector; +end; + +package body util is + function hex_char_to_std_ulogic_vector_tb(hc: character) return std_ulogic_vector is + variable slv: std_ulogic_vector(3 downto 0); + begin + case hc is + when '0' => slv := "0000"; + when '1' => slv := "0001"; + when '2' => slv := "0010"; + when '3' => slv := "0011"; + when '4' => slv := "0100"; + when '5' => slv := "0101"; + when '6' => slv := "0110"; + when '7' => slv := "0111"; + when '8' => slv := "1000"; + when '9' => slv := "1001"; + when 'A' => slv := "1010"; + when 'a' => slv := "1010"; + when 'B' => slv := "1011"; + when 'b' => slv := "1011"; + when 'C' => slv := "1100"; + when 'c' => slv := "1100"; + when 'D' => slv := "1101"; + when 'd' => slv := "1101"; + when 'E' => slv := "1110"; + when 'e' => slv := "1110"; + when 'F' => slv := "1111"; + when 'f' => slv := "1111"; + when others => slv := "XXXX"; + end case; + assert (slv /= "XXXX") report " not a valid hex character: " & hc severity failure; + return slv; + end; +end; + +library ieee, work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use std.textio.all; +use work.util.all; + +entity single_port_block_ram is + generic (g: common_generics; + addr_length: positive := 12; + data_length: positive := 16; + file_name: string := "memory.bin"; + file_type: file_format := FILE_BINARY); + port ( + clk: in std_ulogic; + dwe: in std_ulogic; + dre: in std_ulogic; + addr: in std_ulogic_vector(addr_length - 1 downto 0); + din: in std_ulogic_vector(data_length - 1 downto 0); + dout: out std_ulogic_vector(data_length - 1 downto 0) := (others => '0')); +end entity; + +architecture behav of single_port_block_ram is + constant ram_size: positive := 2 ** addr_length; + + type ram_type is array ((ram_size - 1 ) downto 0) of std_ulogic_vector(data_length - 1 downto 0); + + impure function initialize_ram(the_file_name: in string; the_file_type: in file_format) return ram_type is + variable ram_data: ram_type; + file in_file: text is in the_file_name; + variable input_line: line; + variable tmp: bit_vector(data_length - 1 downto 0); + variable c: character; + variable slv: std_ulogic_vector(data_length - 1 downto 0); + begin + for i in 0 to ram_size - 1 loop + if the_file_type = FILE_NONE then + ram_data(i):=(others => '0'); + elsif not endfile(in_file) then + readline(in_file,input_line); + if the_file_type = FILE_BINARY then + read(input_line, tmp); + ram_data(i) := std_ulogic_vector(to_stdlogicvector(tmp)); + elsif the_file_type = FILE_HEX then -- hexadecimal + assert (data_length mod 4) = 0 report "(data_length%4)!=0" severity failure; + for j in 1 to (data_length/4) loop + c:= input_line((data_length/4) - j + 1); + slv((j*4)-1 downto (j*4)-4) := hex_char_to_std_ulogic_vector_tb(c); + end loop; + ram_data(i) := slv; + else + report "Incorrect file type given: " & file_format'image(the_file_type) severity failure; + end if; + else + ram_data(i) := (others => '0'); + end if; + end loop; + file_close(in_file); + return ram_data; + end function; + + shared variable ram: ram_type := initialize_ram(file_name, file_type); + +begin + block_ram: process(clk) + begin + if rising_edge(clk) then + if dwe = '1' then + ram(to_integer(unsigned(addr))) := din; + end if; + + if dre = '1' then + dout <= ram(to_integer(unsigned(addr))) after g.delay; + else + dout <= (others => '0') after g.delay; + end if; + end if; + end process; +end architecture; + +

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.