Close

Some progress on microcode compiler in C#

zpekiczpekic wrote 04/28/2020 at 19:56 • 4 min read • Like

For some news, check also:

https://hackaday.io/page/11244-more-microcode-compiler-capabilities

https://hackaday.io/project/172073-microcoding-for-fpgas/log/186116-boilerplate-vhdl-code-generation-from-microcode

https://hackaday.io/project/172073-microcoding-for-fpgas/log/197839-more-controllers-implemented-in-microcode


So after some time, I was able to find some time to work on the "MCC" (microcode compiler"). The compiler is now mostly written and is able to compile .mcc files (in a "language" I invented in order to make microcode writing simpler and more intuitive). 

I plan to eventually create a separate project for it with more extensive documentation, but for now here is a excerpt from the microcode for CDP1802/5/6 CPU I use for validation, which should give some idea:

//--------------------------------------------------------
//    1802 basic instructions
//--------------------------------------------------------
            .map 0b0_0000_????;    // D <= M(R(N))
LDN:        direction = mem2any, sel_reg = n, y_bus, reg_d <= alu_y,
            if continue then fetch else dma_or_int;
            
            .map 0b0_0000_0000;    // override for LDN 0
IDL:        sample;
            // dead loop until DMA or INT detected
            if continue then IDL else dma_or_int;

            .map 0b0_0001_????;    // R(N) <= R(N) + 1
INC:        sel_reg = n, reg_inc,
            if continue then fetch else dma_or_int;

            .map 0b0_0010_????;    // R(N) <= R(N) - 1
DEC:         sel_reg = n, reg_dec,
            if continue then fetch else dma_or_int;

            .map 0b0_0011_????;    // 2 byte branch instructions
SBRANCH:    direction = mem2any, sel_reg = p, y_bus, reg_b <= alu_y,    // B <= R(P)
            if cond_3X then sbranch2 else next;
            
skip1:        sel_reg = p, reg_inc,                            // R(P) <= R(P) + 1 (no branch)
            if continue then fetch else dma_or_int;
            
sbranch2:    sel_reg = p, reg_lo <= alu_y, y_b,
            if continue then fetch else dma_or_int;

            .map 0b0_0100_????;                                // D <= M(R(N)), R(N) <= R(N) + 1
LDA:        direction = mem2any, sel_reg = n, y_bus, reg_d <= alu_y,
            goto INC;

            .map 0b0_0101_????;                                // M(R(N)) <= D;
STR:        direction = cpu2mem, sel_reg = n, y_d,        
            if continue then fetch else dma_or_int;

            .map 0b0_0110_0???;
OUT:        sel_reg = x, reg_inc, direction = mem2any,        // DEVICE <= M(R(X)), R(X) <= R(X) + 1
            if continue then fetch else dma_or_int;

            .map 0b0_0110_0000;    // override for OUT 0
IRX:         sel_reg = x, reg_inc,                            // R(X) <= R(X) + 1
            if continue then fetch else dma_or_int;

            .map 0b0_0110_1???;
INP:        sel_reg = x, y_bus, reg_d <= alu_y, direction = dev2any, 
            if continue then fetch else dma_or_int;    

The compiler produces 2 blocks of memory, one for the microcode, and one for the mapper that maps higher level instructions (from CPU instruction set). These blocks are then written in possible formats:

- VDHL - for direct inclusion into FPGA development projects (I use old "free" ISE14.7 from Xilinx)

- Intel HEX - pretty much any tool can consume these

- MIF ("memory initialization file") for Altera tools

- CGF ("core generator file") for Xilinx tools

At this point I am writing / debugging the 1802 CPU which I intend to eventually test using the embedded software (monitor/Basic) documented here. Once it starts (somewhat) working, I will share that project too.

Like

Discussions