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…
Table of Contents |
---|
Main Components
Code Block |
---|
+--------------------------------------------------------------------------------------------+
| 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”
Code Block |
---|
; 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.
historyram0
Code Block |
---|
+-------------------+
clkA | | clkB
-------> <-------
| |
weA | | enaB
------> <-------
| |
enaA | |
------> |
| |
diA[127:0] | historyram0 | doB[7:0]
-------------> --------------->
| |
addrA[9:0] | | addrB[13:0]
------------> <--------------
| |
+-------------------+ |
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).
cpustateram
Code Block |
---|
+-------------------+
(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:
Code Block |
---|
.z
uS Address Rd
15 0000A50E:EA
00 00008100:00
00 00008100:00
00 00008100:00
06 00008100:00
14 0FFF8100:00
15 00008101:00
15 0FFF8101:4C
17 00008102:4C
17 0FFF8102:0D
18 00008103:0D
18 0FFF8103:A5
4F 00008103:A5
4F 0FFF8103:EA
14 0000A50D:EA
14 0FFFA50D:EA |
Signals relating to cpustateram
Code Block |
---|
+--------------------------------------------------------------------+
| monitor_top.v / uart_monitor |
monitor_cpu_state[15:0] | |
------------------------> |
| |
monitor_memory_access_address[31:0] | |
------------------------------------> |
| +----------------+ |
| | monitorctrl | |
| (monitor_cpu_state[15:8])| | |
| cpu_state[7:0] | | |
| -----------------------> | |
| | | |
| cpu_state_write | | |
| <----------------- | |
| | | |
| cpu_state_write_index | | |
| <---------------------- | |
| | | |
| +----------------+ |
| |
| +----------------+ |
| (cpu_state_rdata) | monitorbus | |
| cpu_state | | |
| ----------------> | |
| | | |
| (cpu_address_next[15:0]) | | |
| cpu_address[15:0] | | |
| ----------------> | |
| | | |
| (cpu_di) | | |
| read_data[7:0] | | |
| <--------------- | |
| +----------------+ |
| |
| +----------------+ |
| | cpu6502 | |
| (cpu_address_next[15:0]) | (monitorcpu) | |
| address_next[15:0] | | |
| <------------------- | |
| | | |
| (cpu_di) | | |
| data_i[7:0] | | |
| --------------> | |
| +----------------+ |
| |
+--------------------------------------------------------------------+ |
This was me deep-diving even further to learn more about the signals that relate to cpustateram and how that interact with other components.
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
In “monitor.a65” and the debug_cmd: section, I saw that:
If you type J
in isolation (with no [value] param), it then prints out the current value of two internal variables, as two hex-bytes placed side-by-side.
These internal variables are called:
mon_trace_step
mon_trace
Example 1:
On power-up, typing
J
It will return
0000
I.e.:mon_trace_step
=00
mon_trace
=00
Example 2:
Type
t1
(to turn trace-mode on), then typeJ
It will return
0011
I.e.:mon_trace_step
=00
mon_trace
=11
'J' with parameter
NOTE: ‘J' with parameter only works on my `uartmon_fixes’ branch presently.
If you type J xx
(where ‘xx’ is a hexadecimal byte value):
It sets an the internal variable called
mon_trace
with the xx hex value you specifyIt then prints out the current values of two internal variables, just as it does when 'J' is run without parameters.
Example 3:
Since our prior example had set mon_trace
to be 11
(which turned trace mode on), let’s turn trace mode off, but instead of using the t0
command, let’s do it via J xx
:
type
J 00
It will return
0000
I.e.:mon_trace_step
=00
mon_trace
=00
You can confirm that trace mode has indeed turned off by typing the
r
command repeatedly, and seeing the PC address move around
Example 4:
Let’s now do the opposite. Let’s turn on trace-mode, but instead of using t1
, let’s do it via J xx
:
type
J 11
It will return
0011
You can confirm that trace mode has turned on by typing
r
command repeatedly, and seeing the PC address has halted
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 |
---|---|---|
0 | monitor_break_matched | A flag to indicate whether a breakpoint hit (and genuinely causes the CPU to halt) |
1 | monitor_watch_matched | A flag to monitor whether a watchpoint (data breakpoint) hit (and genuinely causes the CPU to halt) |
2 | monitor_flag_matched | A flag to monitor whether a flag breakpoint hit (and genuinely causes the CPU to halt) |
3 | monitor_watch_en | Seems to be an exact replica of |
4 | monitor_break_en | Seems to be an exact replica of |
5 | 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) |
6 | 0 | Unused. Hardcoded to 0 |
7 | monitor_mem_trace_toggle | This is an output signal from |