![]() |
Forum Index : Microcontroller and PC projects : PicoMite - thinking about PIO - and a request
![]() ![]() ![]() ![]() |
|||||
Author | Message | ||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 4811 |
Please treat below text as thoughts about the PIO compiler. Just thoughts... After doing some reading on the actual implementation in micropython, and reading the RP2040 datasheet, I am not clear on why they split up the code the way they do now. if you look at the PIO sequencer code you need to define upfront how the 5 delay/side bits are to be interpreted. But the definition of this, and the actual assigned PIO pins are not in the sequencer source code, but in the associated python code. This makes going back and forth between PIO code and python code needed to understand what is actually going on. Yeah...I know...old guy, used to looking at a single file program, trying to understand what is going on. How would this work in our MMBasic environment, we would have to put the GPIO assignments in the basic program, and the PIO code in data statements (or separate file) compiled as binaries. Items like SIDESET_COUNT, PINCTRL_SIDESET_COUNT, EXECCTRL_SIDE_EN, PINCTRL_SIDESET_BASE. Since the compiler has to know how to fill the 5 bits delay field, some of these parameters must be known to the compiler before. But some of it must be known/set to the basic program (i.e. the PINCTRL_SIDESET_BASE) because this info is not in the compiled code. Maybe that is because ".side_set 1" is in the compiled code, and (similar/overlapping) the "SIDE_COUNT" in the python code. Anyway, it looks like the compiled sequencer code (the hex file) contains a block of functionality that can be mapped by the python(MMBasic) program towards IO pins/timers. Like a relocatable function block. But there are some setting that must be set the same in PIO code and MMBasic. How do we do that ..? End of thoughts... have to get to work now... PicomiteVGA PETSCII ROBOTS |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
More thoughts: Obviously the use of PIO will not be for the "beginner" so I'm going to expose some of the actual PIO registers to the user as the most powerful way of providing access. So the proposed user I/F is now as follows: SETPIN n,PIO0 SETPIN n,PIO1 configures a pin to allow write access by a PIO program. NB: any pin can be read by a PIO program without specific configuration. The firmware cannot tell if a PIO program tries to read a pin set for another function PIO PROGRAM pionumber, program_array%() Programs one of the two PIO with a specific program. The program_array%() will always be 8 elements long (32x16bit instructions) and unneeded instructions should be filled with MOV y,y (NOP) PIO INIT MACHINE pionumber, clockspeed, EXECCTRL register, SHIFTCTRL register, PINCTRL register, statemachine% This says to set up a statemachine for the specified PIO with a given clock speed. The user is then required to specify the three registers which control how the PIO will operate. See the manual for details. The statemachine% parameter will return the number of the state machine that has been created (1 to 4). It is anticipated that eventually the assembler will be able to generate the register values for the user along with the program array based on the defined assembler directives. NB: I have extensively reviewed both the source of PIOASM and the source of the limited assembler implementation in MicroPython and in both cases trying to integrate the code would be harder than coding from scratch in C and much harder than coding something in Basic PIO START pionumber, statemachine% Starts or restarts a specific state machine PIO STOP pionumber, statemachine% Stops a specific state machine PIO WRITE pionumber, statemachine%, nbr_of_data_elements, data Write data to a specific statemachine. The data format will be the same as the SPI WRITE command i.e. flexible PIO READ pionumber, statemachine%, nbr_of_data_elements, data%(), timeout Read data from a specific statemachine. The data format will be the same as the SPI READ command i.e. a single integer array. The timeout specifies the time to wait in milliseconds for data to be available Thoughts? Edited 2021-06-28 19:40 by matherp |
||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 4811 |
Hi Peter, Please explain the format of program_array%. Do you expect the PIO code to be formatted as 64 bit integers in this array (8 x 64 bit = 32 x 16 bit). Which 16 bit value goes where (endianness). Please explain the reason to fill the rest of the array with MON y,y. Since the machine never get's there, why not JMP 0 (= &h0). No problem, just curious... I like the idea of the "basic compiler" since (Mixtel90 showed) it is do-able, maintainable, and this step could also be replaced by using external compilers. As long as they generate a hex file. Do you see an option for the compiler (basic) to become an integral part of the picomite ? In the MX170 we have a "library" where we can store basic functions that we use often, and these where available from the running program. If that where the case the library could contain the compiler, and we could have the PIO source code in our basic program (i.e. as data statements), could compile "on the fly", and plunge the resulting hex into the programming array%. You could even imagine using slot #0 in flash for that (for the library), or would you need the library to be in RAM also ? Regards, Volhout PicomiteVGA PETSCII ROBOTS |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
Yes, normal endidness Could be either - just needs to be a valid instruction TBD The concept of a library doesn't make much sense since we are running from RAM. You could though store the Basic assembler in a flash slot and chain to and from it to load up the PIO programs. This is functionality that should work now. |
||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 4811 |
Hi Peter, You made the request about PIO in this thread, and added the new code in the LCD display thread. Kind of confusing. I only played a bit with CHAIN, but AFAIK the chain does not return to the program it was called from. So invoking the compiler from a chain, need you to edit the compiler itself to make it return to the program you called it from. Or split up your program in 2 parts (to avoid re-dimensioning variables if you return from the chain). Part 1 does the DIM's , and inits for PIO, then chain the compiler, then chain the execution part of your program. The hard reference to a flash slot can help in that effect. If you always have your execution part of the program in slot 1, then the compiler can be hard coded to execute slot 1 after compilation. PicomiteVGA PETSCII ROBOTS |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
I put the code in the code thread I'll add a system variable that tells the program which flash slot it was loaded from. That way a program can set a variable to tell a chained program where to next chain |
||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 4811 |
Hi Peter, Maybe an idea to add the system variable that shows the previous flash slot. I.e. MM.chain -1 : it is an initial run 0-10 : the slot that called the current program. That way you can skip de DIM statements 'code in any flash slot if MM.chain < 0 then DIM a%(100) flash chain 1 end if print a%(1) 'in flash slot #1 a%(1)=23 flash chain MM.chain Edited 2021-06-28 23:26 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7458 |
Latest version PIO assembler wip015.zip A lot has changed. At the expense of clarity, I'm pretty sure that it's smaller (and maybe slightly slower) than previously. The assembler is now a proper Sub to make it easier to use. Will now save to the required array format. I'm currently working on having 2 sets of data statements and two output arrays, allowing it to assemble the code for both PIO progs in the same program. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
Mick Great work but another request - sorry. The PIO have a facility where you can force a specific instruction on to them which executes immediately. The various supplied examples use this to change the way execution is happening. I'm incorporating a new sub-command to support this PIO EXECUTE pionumber,statemachinenumber, instruction To be fully integrated we will need a instruction function that returns a single integer e.g. ? PIOinstruction("PULL ifempty,block") Then we can execute a Basic statement like CODE]PIO EXECUTE pionumber,statemachinenumber, PIOinstruction("PULL ifempty,block") Later today I'll post a bunch of the example PIO programs and the correct machine code for you to help testing Edited 2021-06-29 17:39 by matherp |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7458 |
Thanks. It'll help a lot. :) The assembler should recognise now. (p346 of the RP2040 dada sheet) Load a 32-bit word from the TX FIFO into the OSR. pull <ifempty> block where <ifempty> defaults to 0 and block defaults to 1 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
Sample 1 0x9080, // 0: pull noblock side 0 0xa027, // 1: mov x, osr 0xa046, // 2: mov y, isr 0x00a5, // 3: jmp x != y, 5 0x1806, // 4: jmp 6 side 1 0xa042, // 5: nop 0x0083, // 6: jmp y--, 3 Sample 2 0xf780, // 0: set pindirs, 0 side 0 [7] 0xf781, // 1: set pindirs, 1 side 0 [7] 0xff80, // 2: set pindirs, 0 side 1 [7] 0xff81, // 3: set pindirs, 1 side 1 [7] Sample 3 0x008c, // 0: jmp y--, 12 0xc030, // 1: irq wait 0 rel 0xe027, // 2: set x, 7 0x6781, // 3: out pindirs, 1 [7] 0xba42, // 4: nop side 1 [2] 0x24a1, // 5: wait 1 pin, 1 [4] 0x4701, // 6: in pins, 1 [7] 0x1743, // 7: jmp x--, 3 side 0 [7] 0x6781, // 8: out pindirs, 1 [7] 0xbf42, // 9: nop side 1 [7] 0x27a1, // 10: wait 1 pin, 1 [7] 0x12c0, // 11: jmp pin, 0 side 0 [2] // .wrap_target 0x6026, // 12: out x, 6 0x6041, // 13: out y, 1 0x0022, // 14: jmp !x, 2 0x6060, // 15: out null, 32 0x60f0, // 16: out exec, 16 0x0050, // 17: jmp x--, 16 // .wrap Sample 4 0x7821, // 0: out x, 1 side 1 0x0623, // 1: jmp !x, 3 [6] 0x1700, // 2: jmp 0 side 0 [7] 0x0704, // 3: jmp 4 [7] 0x7021, // 4: out x, 1 side 0 0x0627, // 5: jmp !x, 7 [6] 0x1f04, // 6: jmp 4 side 1 [7] 0x0700, // 7: jmp 0 [7] Sample 5 0x2ba0, // 0: wait 1 pin, 0 [11] 0x00c4, // 1: jmp pin, 4 0x4021, // 2: in x, 1 0x0000, // 3: jmp 0 0x4141, // 4: in y, 1 [1] // .wrap_target 0x2b20, // 5: wait 0 pin, 0 [11] 0x00c9, // 6: jmp pin, 9 0x4041, // 7: in y, 1 0x0000, // 8: jmp 0 0x4121, // 9: in x, 1 [1] // .wrap Sample 6 // .wrap_target 0x80a0, // 0: pull block side 0 0x40e1, // 1: in osr, 1 side 0 0x6068, // 2: out null, 8 side 0 0x40e1, // 3: in osr, 1 side 0 0x6068, // 4: out null, 8 side 0 0x40e1, // 5: in osr, 1 side 0 0x6060, // 6: out null, 32 side 0 0x80a0, // 7: pull block side 0 0x50e1, // 8: in osr, 1 side 1 0x7068, // 9: out null, 8 side 1 0x50e1, // 10: in osr, 1 side 1 0x7068, // 11: out null, 8 side 1 0x50e1, // 12: in osr, 1 side 1 0x7060, // 13: out null, 32 side 1 0x507a, // 14: in null, 26 side 1 0xb016, // 15: mov pins, ::isr side 1 // .wrap Sample 7 // .wrap_target 0x80e0, // 0: pull ifempty block 0xe022, // 1: set x, 2 0x40e5, // 2: in osr, 5 0x6065, // 3: out null, 5 0x4063, // 4: in null, 3 0x0042, // 5: jmp x--, 2 0x4048, // 6: in y, 8 0xa0d6, // 7: mov isr, ::isr 0x6061, // 8: out null, 1 0xe03f, // 9: set x, 31 0xe000, // 10: set pins, 0 0xa606, // 11: mov pins, isr [6] 0xe001, // 12: set pins, 1 0x46c1, // 13: in isr, 1 [6] 0x004a, // 14: jmp x--, 10 // .wrap Sample 8 // .wrap_target 0x80a0, // 0: pull block 0xa02f, // 1: mov x, !osr 0x80a0, // 2: pull block 0xa047, // 3: mov y, osr 0x0006, // 4: jmp 6 0x0046, // 5: jmp x--, 6 0x0085, // 6: jmp y--, 5 0xa0c9, // 7: mov isr, !x 0x8020, // 8: push block // .wrap Sample 9 0x2ba0, // 0: wait 1 pin, 0 [11] 0x00c4, // 1: jmp pin, 4 0x4021, // 2: in x, 1 0x0000, // 3: jmp 0 0x4141, // 4: in y, 1 [1] // .wrap_target 0x2b20, // 5: wait 0 pin, 0 [11] 0x00c9, // 6: jmp pin, 9 0x4041, // 7: in y, 1 0x0000, // 8: jmp 0 0x4121, // 9: in x, 1 [1] // .wrap Sample 10 // .wrap_target 0x7821, // 0: out x, 1 side 1 0x0623, // 1: jmp !x, 3 [6] 0x1700, // 2: jmp 0 side 0 [7] 0x0704, // 3: jmp 4 [7] 0x7021, // 4: out x, 1 side 0 0x0627, // 5: jmp !x, 7 [6] 0x1f04, // 6: jmp 4 side 1 [7] 0x0700, // 7: jmp 0 [7] // .wrap } |
||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 4811 |
@Mixtel: interesting concept using the Instr function that returns the pointer to the found match, then divide by the element size .... as a "decoder". Replaces the search in a linked list or structure. PicomiteVGA PETSCII ROBOTS |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7458 |
side & [ ] now working. It now passes Sample 1, Sample 2 and most of Sample 3. irq and .wrap not implemented at all yet. irq coming soon, it shouldn't be so impatient! out null,32 won't assemble as there are only 5 data bits. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
Volhout Guru ![]() Joined: 05/03/2018 Location: NetherlandsPosts: 4811 |
Hi Mixtel90, I am impressed, you took up the glove, and are really making the whole compiler !! Great work, and thank you !!! About out null,32: So you shift out 32 bits from the OSR, basically clearing the OSR. Volhout P.S. While reading though the sequencer instruction set, I get scared. You can write really confusing code using this set. You can interpret data in the shift registers as instruction, or as direct jump address. So you can write a sequence that reads serial data in the ISR, passes it to the OSR, and executes it..... scary... I guess next years programming challenge will be to write the most obfuscated program for the PIO sequencer on the picomite.... Edited 2021-06-30 16:48 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7458 |
At the moment I'm simply taking strings from the data statements and converting them into 16-bit words for the RP2040 to process. Unless it's something that I can do that with I'm a bit stuck. That's why I've been thinking of it as an assembler. A compiler would probably be the high level PIO commands that Peter's doing. I should have seen that - probably got too bog-eyed. lol In this case It's easy enough, I can encode 32 as &b00000 easily enough, but I can't (at the moment) handle stuff like .wrap as that's a directive that does something that isn't a command as such (it's a backward jump, but you can't assemble it as that because it's much faster). I've no directives at the moment and no idea how to implement them. I couldn't activate autopull if it had a big on-off switch on it. :) Yep - this thing is seriously scary. :) Even the concept of 4 state machines independently working from the same 32-byte memory is a little bit scary... Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
All the directives translate into bit settings in the PINCTRL, EXECCTRL, and SHIFTCTRL registers. If your assembler could return values for these (could be in like named Global variables) then this would complete the loop. The user would then just include these in the PIO INIT MACHINE command |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7458 |
Is there a map of those registers somewhere, Peter? I couldn't see them by those names in the datasheet. If I'm correct, each status machine has it's own register set. The IRQ is *still* waiting to be processed... :) Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
rp2040-datasheet.pdf Section 3.7 Edited 2021-07-01 03:51 by matherp |
||||
Mixtel90![]() Guru ![]() Joined: 05/10/2019 Location: United KingdomPosts: 7458 |
I looked there... I assume the ones I want are: SM0_EXECCTRL SM0_SHIFTCTRL SM0_PINCTRL where SM0, SM1, SM2 and SM3 are the state machines. I was looking for some sort of "general" rather than SM-specific ones. IRQ assembles. .wrap target and .wrap now store their discovered addresses (next and previous respectively) in variables test printout much improved! Edited 2021-07-01 07:22 by Mixtel90 Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 9992 |
Correct, the state machines run the same code but they can differ in clock speed and pin usage and even which bit of the code they run. So effectively there could be 4 different (but very short) programs for each PIO with each state-machine running a different program by using different entry points to the code for their pio |
||||
![]() ![]() ![]() ![]() |
![]() |