UART Monitor Studies
Been doing some digging into how the uart monitor works currently.
Will paste a few ascii diagrams I was accumulating on how the components work together…
Main Components
+--------------------------------------------------------------------------------------------+
| machine.vhdl |
| |
| +------------------------------------------------------------------------------------+ |
| | monitor_top.v = uart_monitor | |
| | | |
| | +--------------+ | |
| | | monitor_ctrl | | |
| | +--------------+ | |
| | | |
| | +--------------+ | |
| | | monitor_bus | | |
| | +--------------+ | |
| | 1024 x 16 bytes 1024 x 8 bytes 16 x 8 bytes | |
| | +--------------+ +---------------+ +---------------+ +--------------+ | |
| | | monitor_mem | | asym_ram_sdp | | asym_ram_sdp | | asym_ram_sdp | | |
| | | (monitor.a65)| | (historyram0) | | (historyram1) | | (cpustateram)| | |
| | +--------------+ +---------------+ +---------------+ +--------------+ | |
| | | |
| | +--------------+ | |
| | | cpu6502 | | |
| | +--------------+ | |
| | | |
| +------------------------------------------------------------------------------------+ |
| |
+--------------------------------------------------------------------------------------------+
The uart-monitor is presently written in verilog, with the parent file named "monitor_top.v"
It defines a component called "uart_monitor"
"machine.vhdl" makes use of this uart_monitor component, feeds various signals between it and the 4510 cpu.
Inside the uart-monitor is a 6502 cpu component (also written in verilog).
It runs the code that resides within the "monitor_mem" component (which is just the compiled assembly contained within "monitor.a65")
There are various other ram components (e.g., historyram0/1 and cpustateram) that get mapped in a way dictated by the early comment found in “monitor.a65”
; monitor memory map
;
; $0000-$00ff - zero page
; $0100-$01ff - stack
;
; $7000-$7fff - CPU State History (16 entries, 8 bytes each)
; $8000-$8017 - history memory read window (read only)
;
; $9000-$97ff - monitor hardware control registers
;
; $f000-$ffff - 4K Monitor ROM/RAM, also mirrored at $0000-$0fff
The actual mapping logic is orchestrated by the “monitor_bus” component.
cpustateram
+-------------------+
(clock) clkA | | clkB (clock)
-------> <-------
| |
(cpu_state_write) weA | | enaB (1)
------> <-------
| |
(1) enaA | |
------> |
{ | |
(0 [15:0]) | |
(monitor_cpu_state[15:0]) | |
(monitor_memory_access_address[31:0])| |
} --> diA[63:0] | cpustateram | doB[7:0] (cpu_state_rdata[7:0])
-------------> --------------->
| |
(cpu_state_write_index) addrA[3:0] | | addrB[6:0] (cpu_address_next[6:0])
------------> <--------------
| |
+-------------------+
This ‘cpustateram' component gets mapped to that $7000-$7fff region (from the 6502 cpu’s addressing perspective).
It's a dual-port ram, where I've put:
stuff on the left relating to signals wanting to write data into the ram.
stuff on the right relating to signals wanting to read data from the ram.
So on the left-side (write), seems like the idea is to write a big chunk of cpu-state related information within a single clock cycle.
monitor_cpu_state (16-bit, one byte is the internal cpu-state details, and the other byte is the currently read byte, I think)
monitor_memory_access_address (32-bit), the current address being accessed.
On the right-side (read), this is to let this 6502 monitor cpu read out this data via an 8-bit bus (a byte at a time).
So, the assembly code within "monitor.a65" will read from $7000-$7fff to try retrieve this cpu-state trace history.
This seems to be the data that is used to generate the output from the uart-monitor's z command. I.e., this stuff:
Signals relating to cpustateram
This was me deep-diving even further to learn more about the signals that relate to cpustateram and how that interact with other components.
historyram0 and historyram1
I haven’t dived too deep into historyram0 and historyram1 as yet, though from my initial gleaming of it, historyram was trying to be more ambitious with the volume of data it captured in each trace entry (current x,y,z,a and a lot more).
Hence the reason why the left-side (write) has a data-bus size of 128-bits (can write 16-bytes per clock cycle).
I believe it relates to using the z xx
command. I.e., z with a hex-byte parameter. I think the parameter is the index of the trace-entry within the ram to read from, I believe it will then print register-information just as is done with the r command, but making use of the trace-entry values for these register values had at the time of the trace.
Deciphering the 'J' command
The 'J' command gets succinctly described in the manual as:
J : DEBUGMON
Format: j [value]
Usage: Display, and optionally set, internal signals of the matrix mode monitor
interface.
I’ve been studying various files to get a better feel for what this really does…
'J' without parameter | 'J' with parameter |
---|---|
In “monitor.a65” and the debug_cmd: section, I saw that: If you type
Example 1:
Example 2:
| NOTE: ‘J' with parameter only works on my `uartmon_fixes’ branch presently. If you type
Example 3: Since our prior example had set
Example 4: Let’s now do the opposite. Let’s turn on trace-mode, but instead of using
|
What is the ‘mon_trace’ variable?
When you set the value of this mon_trace
variable, it actually sets a byte register inside monitor_ctrl.v
called mem_trace_reg
with the value you specify.
This register turns out to be a bitfield consisting of:
BIT | FIELD NAME | PURPOSE |
---|---|---|
0 | monitor_mem_trace_mode | This signal is an output from Best I can fathom is that this bit dictates to the CPU if it needs to halt or not. |
1 | monitor_flag_en | This signal dictates whether the cpu will be permitted to halt if a flag-breakpoint has been hit or not. I.e., you may have set a flagwatch via the The |
2 | history_write | I ‘think’ this a flag to dictate whether trace info is written into the historyram0/1 components or not. If the bit is set, after 1023 trace entries of the ram have been filled, no more trace entries are recorded, and this bit will be automatically reset. |
3 | history_write_continuous | This flag is set (in conjunction with history_write), then trace entries will be recorded perpetually. Once the 1023rd entry is filled, following trace entries will be written from entry position#0 and onwards (looping in a circular fashion). |
4 | monitor_irq_inhibit | This bit is output from |
5 | monitor_hyper_trap | (unused) Has been hardcoded inside The signal gets routed to |
6 | monitor_watch_en | This bit dictates whether the monitor is allowed to halt the cpu if it detects that the user’s watchpoint (data-breakpoint) has hit. Such watchpoints can be set via the |
7 | monitor_break_en | This bit dictates whether the monitor is allowed to halt the cpu if it detects that the user’s breakpoint address has hit. Such breakpoints can be set via the |
What is the ‘mon_trace_step’ variable?
The mon_trace_step
variable seems to be only writable by the uart-monitor system itself. We can not write to it via the J
command, but simply keep track of what its value currently is. It appears to be bitfield containing various other internal monitor signals (some which seem closely related to those in mon_trace
).
This register turns out to be a bitfield consisting of:
BIT | FIELD NAME | PURPOSE |
---|---|---|
7 | monitor_break_matched | A flag to indicate whether a breakpoint hit (and genuinely causes the CPU to halt) |
6 | monitor_watch_matched | A flag to monitor whether a watchpoint (data breakpoint) hit (and genuinely causes the CPU to halt) |
5 | monitor_flag_matched | A flag to monitor whether a flag breakpoint hit (and genuinely causes the CPU to halt) |
4 | monitor_watch_en | Seems to be an exact replica of |
3 | monitor_break_en | Seems to be an exact replica of |
2 | monitor_watch_match | This is a signal that is input into (this can go high even if the CPU wasn’t halted as a consequence) |
1 | 0 | Unused. Hardcoded to 0 |
0 | monitor_mem_trace_toggle | This is an output signal from |