Close

Operation mode 1 - Parse ASCII stream in Intel HEX format, and write to memory

A project log for Custom circuit testing using Intel HEX files

Download / upload memory contents into computer motherboards or other devices for test or debugging (using 3 micro-coded controllers)

zpekiczpekic 12/08/2021 at 05:320 Comments

The key component here is Hex2Mem which I intend to document better on its own page. But few explanations here until I get around to do it. Refer to slightly modified VHDL and microcode

Basic operation is as follows:

  1. Wait for ASCII character
  2. If there is one, branch to location that processes it (the ASCII code can be thought of as an "instruction")
  3. If invalid, output error, go to step 1
  4. If valid, process it based on which it is and what is expected or not (for example ":" can come only once at the beginning of line, spaces or tabs anywhere but will be ignored, unless they are between hex digits that should not be split (e.g. data bytes)
  5. Each two digits are written into one internal byte memory location (there is a small 64 bytes buffer)
  6. As a byte is written into internal RAM, the checksum is updated
  7. The number of bytes received is checked with expected record length, for error check
  8. Final byte received is the checksum. Added to accumulated checksum it should result in 0x00 in the LSB of the checksum register
  9. If checksum is correct, the data bytes are written in a burst to external RAM bus. This means RAM will not be thrashed by bad checksum record
  10. Either CR and/or LF indicates end of record, this increments the line counter, clears the character counter (these are only used to show error message) and processing of new record can start.

This is how it is hooked up into the design:

hexin: hex2mem Port map (
            clk => hex_clk,
            reset_in => reset,
            reset_out => open,
            reset_page => page_sel, -- not really used but i8080-like system would reset at lowest 8k updated
            --
            debug => hexin_debug(15 downto 0),
            --
            nWR => nWrite,
            nBUSREQ => hexin_busreq,
            nBUSACK => hexin_busack,
            nWAIT => nWait,
            ABUS => ABUS,
            DBUS => DOUT,
            BUSY => hexin_busy,    -- yellow LED when busy
            --
            HEXIN_READY => hexin_ready,
            HEXIN_CHAR    => hexin_char,
            HEXIN_ZERO => open,
            --
            TRACE_ERROR => dip_traceerror, 
            TRACE_WRITE => dip_tracewrite, 
            TRACE_CHAR    => dip_tracechar, 
            ERROR => LDT2R,    -- red LED when error detected
            TXDREADY => tty_sent,
            TXDSEND => hexin_debug_send,
            TXDCHAR => hexin_debug_char
        );

Signals:

The video is a shaky recording of a session to input from a test HEX file into the memory. It wasn't successful because I forgot to clear the wait mode, so the component was stuck waiting to write a byte (false condition, so repeat kept executing):

        // ask CPU for memory, then write 1 byte with any number of optional wait cycles
writemem:    ram_addr = bytecnt, nBUSREQ = 0;
        ram_addr = bytecnt, nBUSREQ = 0, if nBUSACK then repeat else next;
        ram_addr = bytecnt, nBUSREQ = 0, nWR = 0;
        ram_addr = bytecnt, nBUSREQ = 0, nWR = 0, if nWAIT then next else repeat;

Finally, I typed a few random characters to show how it detected bad input and emitted error message about it:

//    error codes are 1 to 6, 0 means no error
errcode:        .regfield 3 values
            ok, 
            err_badchar,        // ERR1
            err_unexpected,        // ERR2
            err_badchecksum,    // ERR3
            err_badrecordtype,    // ERR4
            err_badrecordlength,    // ERR5
            same
            default same;

While I was fiddling with WAIT, the host was sending data, and because there is no handshake, many bytes got lost. Eventually it sync'd up with ":" record start character and after that it wrote to RAM and output the trace:

            if TRACE_WRITE then next else nextaddr; 
            emit(char_A);        // A[address]=data
            emit(char_open);
            printaddr();
            emit(char_close);
            printram();


The wait circuit is implemented in top level component, because it is reused by HEX2MEM and MEM2HEX. It is triggered by either component activating nRD or nWR signal (nAccess signal). That means memory operation is requested. If the WAIT is enabled (a S/R flip/flop controls that) then nWAIT is locked low until a button is pressed. This way each memory access can be inspected (the A and DBUS values appear on the 7seg LED which is conveniently 6 digits on Anvyl so 4 hex A and 2 hex DBUS can be displayed).

 The FF below has a little trick - the clock itself is multiplexed depending on its state. When not in WAIT mode (nWait = '1') it will be triggered on nRD or nWR going low, but  once waiting, then press on the button(3) flips in around. Therefore:

-- Wait signal
wait_ena <= not (reset or reset_sw or button(2) or wait_dis);
wait_dis <= not (button(1) or wait_ena);

wait_clk <= (not nAccess) when (nWait = '1') else button(3);
on_wait_clk: process(reset, wait_clk)
begin
    if (wait_dis = '1') then
        nWait <= '1';
    else
        if (rising_edge(wait_clk)) then 
            nWait <= not nWait; 
        end if;
    end if;
end process;

Discussions