I use DMA on most PIC18f projects with the current fav being the Q84 series because of CANFD or the K42(3) when I don't need CAN. The arbitration logic is simple but the usable memory bus bandwidth is limited because it's lacking a true memory fiber cross-connect you see on 32-bit parts.The DMA on the newer 18f parts is pretty cool. I combined the NCO (numerically controlled oscillator) with the DMA and DAC to implement a gated arbitrary waveform generator (DC to 2Mhz) that requires zero instruction cycles to run.
These parts/peripherals weren't available to me under the old MPLab, so that is the silver lining (of having to switched to C).
Unfortunately, the C setup code footprint is still at least twice as large as if I coded it in .asm, but at least it's run once and forget.
The on-chip DAC is only 5 bits, and there's a small amount of jitter on the NCO, so there is some distortion on the output.
The datasheet doesn't say when "bubbles" become available, nor the maximum latency between the transaction trigger and the actual transaction.I use DMA on most PIC18f projects with the current fav being the Q84 series because of CANFD or the K42(3) when I don't need CAN. The arbitration logic is simple but the usable memory bus bandwidth is limited because it's lacking a true memory fiber cross-connect you see on 32-bit parts.
https://raw.githubusercontent.com/nsaspook/vtouch_v2/eadogs/q43_board/q43_ntsc.X/ntsc.c
https://ww1.microchip.com/downloads...emory-Access-on-8-bit-PIC-MCU-DS90003164B.pdf
As shown in Figure 1, each DMA controller can be independently configured to move data between single or multiple addresses. The DMA controllers use the same instruction bus and data bus as the CPU for transferring data between memories. Depending on the priority set in the system arbiter, the DMA controller can move data either by utilizing unused CPU cycles or stalling the CPU. By default, the CPU has priority over DMA. In this case, the DMA steals unused cycles from the CPU to perform the read/write operations. Because of this concurrent operation between the CPU and DMA, the bandwidth of handling data is increased and the DMA module can operate in the background.
Not much information out there about the low-level details of PIC18 System Arbitration.The datasheet doesn't say when "bubbles" become available, nor the maximum latency between the transaction trigger and the actual transaction.
This doesn't seem to matter in my immediate application, but I'd sure like to know -- if only for academic reasons. Any insights?
I use a few tricks to get around 8-bit DMA shortcomings.The system arbiter resolves memory access between the system level selections (i.e., Main, Interrupt Service Routine) and peripheral selection (e.g., DMA and Scanner) based on user-assigned priorities. A block diagram of the system arbiter can be found below. Each of the system level and peripheral selections has its own priority selection registers. Memory access priority is resolved using the number written to the corresponding Priority registers, 0 being the highest priority selection and the maximum value being the lowest priority. All system level and peripheral level selections default to the lowest priority configuration. If the same value is in two or more Priority registers, priority is given to the higher-listed selection according to the following table.
The remaining problem is dynamic updates to the video memory. All of the memory bandwidth (something in short supply with the simple DMA architecture seen in most 8-bit controllers) is being used by DMA so excessive processing during DMA will affect H/V timing causing tearing and loss of sync. The fix for that is to only process mainline code during the scanlines with no video around the H sync pulse. For that we need a simple task manager that uses the high/low priority interrupt structure and a 'hold' flag.
Setup a Low priority timer ISR as the 'idle' loop; This idle loop can be interrupted by High priority DMA completion and other module interrupt requests so those time critical processes get the cpu they need during the NTSC FSM. The 'idle' loop can also set a task time duration for the main task that will interrupt the main task back to the low priority idle loop during the critical timing periods. This means I can step into the main application code execution when I want it to run it and control how long I want one processing time slice to be.
uint8_t ble_wv_data[20];
uint8_t ble_wv_data_index;
...
int8_t value;
...
ble_wv_data[ble_wv_data_index >> 1] = ((uint8_t)value) << 4;
BLE.c:325:68: warning: implicit conversion loses integer precision: 'int' to 'uint8_t' (aka 'unsigned char') [-Wconversion]
ble_wv_data[ble_wv_data_index >> 1] = ((uint8_t)value) << 4;
~ ~~~~~~~~~~~~~~~~~^~~~
ble_wv_data[ble_wv_data_index >> 1] = ble_wv_data[ble_wv_data_index >> 1] | (uint8_t)value;
Default integer promotions to 16-bits.C:uint8_t ble_wv_data[20]; uint8_t ble_wv_data_index; ... int8_t value; ... ble_wv_data[ble_wv_data_index >> 1] = ((uint8_t)value) << 4;
produces the warning:
but this does not:Code:BLE.c:325:68: warning: implicit conversion loses integer precision: 'int' to 'uint8_t' (aka 'unsigned char') [-Wconversion] ble_wv_data[ble_wv_data_index >> 1] = ((uint8_t)value) << 4; ~ ~~~~~~~~~~~~~~~~~^~~~
Why? Does the shift only operate on signed values?C:ble_wv_data[ble_wv_data_index >> 1] = ble_wv_data[ble_wv_data_index >> 1] | (uint8_t)value;
Disassembly Listing for ip_test
Generated From:
/root/HarmonyProjects/ip_test.X/dist/default/production/ip_test.X.production.elf
May 6, 2024 3:28:54 PM
--- /tmp/xcXelNgbB/driver_tmp_1.s ---------------------------------------------------------------------
3702 0E32 MOVLW 0x32
3704 6EF6 MOVWF 0xFF6, ACCESS
3706 0E37 MOVLW 0x37
3708 6EF7 MOVWF 0xFF7, ACCESS
370A 0E00 MOVLW 0x0
370C 6EF8 MOVWF 0xFF8, ACCESS
370E 0009 TBLRD*+
3710 C4F5 MOVFF TABLAT, __pdataCOMRAM
3714 0009 TBLRD*+
3716 C4F5 MOVFF TABLAT, _ble_wv_data_index
371A EE01 LFSR 0, 0x501
371E 0E14 MOVLW 0x14
3720 6AEE CLRF 0xFEE, ACCESS
3722 06E8 DECF 0xFE8, F, ACCESS
3724 E1FD BNZ 0x3720
3726 0100 MOVLB 0x0
3728 EF96 GOTO 0x372C
--- /root/HarmonyProjects/ip_test.X/main.c ------------------------------------------------------------
1:
2:
3: #include "mcc_generated_files/mcc.h"
4: uint8_t ble_wv_data[20];
5: uint8_t ble_wv_data_index = 1;
6: int8_t value = 1;
7:
8: /*
9: Main application
10: */
11: void main(void)
12: {
13:
14:
15: ble_wv_data[ble_wv_data_index >> 1] = (uint8_t) (((uint8_t) value) << 4);
372C 0E10 MOVLW 0x10
372E 6E01 MOVWF __mediumconst, ACCESS
16:
17: while (1);
3730 D7FF BRA 0x3730
18: }
19: /**
20: End of File
21: */
Understood...thanks.Default integer promotions to 16-bits.
...
Not sure on that one.Understood...thanks.
Just one more thing to keep in mind.
BTW, is there something special about curly braces inside a string that initializes a const char array?
For example, "{xyz}" doesn't produce the expected code, but "\{xyz}" does.
As you can see, this ain't my first rodeo with these types of issues.Nice article from your other thread, @nsaspook: https://barrgroup.com/blog/efficient-c-code-8-bit-microcontrollers
This should be required reading for anyone writing 8 bit code in C.
All of his frustrations are mine.
I still think the hoops one needs to jump through to get good compiled code is hopelessly annoying and time consuming.As you can see, this ain't my first rodeo with these types of issues.
I still think the hoops one needs to jump through to get good compiled code is hopelessly annoying and time consuming.
I'd not voluntarily work this way if I had other options.

I do.Assembly is the raw, ultimate power, but most people wouldn't know what to do with it.
I don't believe that....
Assembly is the raw, ultimate power, but most people wouldn't know what to do with it.
Whatever.I don't believe that.
If it was something special in the general sense, showing programming skill or power, people would be using it today for just about every task for bragging rights instead of using it when actually needed. Most people (programmers) don't need Assembly because the work of Assembly has already been done on the OS/Programming language they use. So, they, like most sensible people, delegate assembly to the compiler system that's almost as good as the best human assembly coders because coding in X or X language is only a small part of programming. Small 8-bit embedded programming is a corner case for C but IMO with the modifications to a compiler like XC8, that's optimized for the 8-bit universe and the knowledge of what it takes to optimize C source code in that 8-bit universe to make optimized assembly using that compiler The ability to easily transport that same code to more complex systems is worth the time and effort of using C over Assembly for all but the most sensitive to hardware code sections.
If all of your systems are 8-bit, of the same architect and controller type then coding in assembly is a good choice but the world of programming embedded systems is much larger than that little corner.
If there was a great need and demand, it would be created by someone to make a buck. That's how capitalism works.Whatever.
Still no excuse for not having a good assembler for those who choose to write .asm, whatever their reason.
You're right. I've been tempted to write one. I just wouldn't know how to integrate it with NetBeans.If there was a great need and demand, it would be created by someone to make a buck. That's how capitalism works.
BLE_FLAG_NOTIFY = ~BLE_FLAG_NOTIFY;
BLE_FLAG_NOTIFY = !BLE_FLAG_NOTIFY;
11C9C A438 BTFSS ble_scan_flags, 2, ACCESS
11C9E D002 BRA 0x1CA4
11CA0 0E01 MOVLW 0x1
11CA2 D001 BRA 0x1CA6
11CA4 0E00 MOVLW 0x0
11CA6 0AFF XORLW 0xFF
11CA8 6E1D MOVWF potnum, ACCESS
11CAA 461D RLNCF potnum, F, ACCESS
11CAC 461D RLNCF potnum, F, ACCESS
11CAE 5038 MOVF ble_scan_flags, W, ACCESS
11CB0 181D XORWF potnum, W, ACCESS
11CB2 0BFB ANDLW 0xFB
11CB4 181D XORWF potnum, W, ACCESS
11CB6 6E38 MOVWF ble_scan_flags, ACCESS
BLE_FLAG_NOTIFY ^= 1;
11F5E 7438 BTG ble_scan_flags, 2, ACCESS