My work will follow this mailbox format; it sounds like a lot of Ray's will as well - perhaps others will use it as well.
Where is the definitive description of "this mailbox format"? I've seen a lot of discussion here but is there a place to go to get the current description of what you're proposing? Is the top post being updated with the latest thinking?
I'm not trying to be a naysayer, just trying to get more details out in the open so we can either get buy-in or see what needs tweaking.
1) With 6 bits for the DRIVERID (I suggest DRIVERTYPE) we are limited to 32 types. That will likely cover the common cases, but there will obviously be more than 32 driver types in use. Would we have a type "other" and then a function call to an DRIVERINFO function be used to get extended info.
2) What is responsible for setting up the mailbox? Does the driver search the bit field mentioned earlier and find the next open slot, or is the application loading the driver responsible for doing that? My thought is that the loader is responsible.
Where is the definitive description of "this mailbox format"? I've seen a lot of discussion here but is there a place to go to get the current description of what you're proposing? Is the top post being updated with the latest thinking?
"generic" mailbox format
long 0 - function/status
long 1 - from pointer
long 2 - to pointer
long 3 - length/size
cogid - ID of cog running the driver associated with this mailbox
driverid - type of driver (serial, i2c, spi, vga, hdtv, ntsc, pal, etc - more like class of driver) - allows 64 different types of drivers, reserved could be used to extend this
reserved - possibly later used to give info like number of serial ports, etc
Low word in long:
7 bits of status - command completed OK, command error, char available etc
9 bits of data - normally only 8 used, but 9 bits useful to pass "null" byte for drivers polling word for 0 value
The following three longs, whenever reasonable, would be the pointers to/from the data, and size of data; however specific drivers can use them differently if the driver does not fit a simplified unix-like driver model (open, stat, ioctl, read, write, close)
stat messages can get extended information from a driver
ioctl can set driver parameters
read/write would use the length parameter to specify size of transaction
Almost all drivers fit this model nicely, and extensions are possible through custom messages.
Driverid will likely be renamed DriverClass or DriverType
cogid - ID of cog running the driver associated with this mailbox
driverid - type of driver (serial, i2c, spi, vga, hdtv, ntsc, pal, etc - more like class of driver) - allows 64 different types of drivers, reserved could be used to extend this
reserved - possibly later used to give info like number of serial ports, etc
Low word in long:
7 bits of status - command completed OK, command error, char available etc
9 bits of data - normally only 8 used, but 9 bits useful to pass "null" byte for drivers polling word for 0 value
The following three longs, whenever reasonable, would be the pointers to/from the data, and size of data; however specific drivers can use them differently if the driver does not fit a simplified unix-like driver model (open, stat, ioctl, read, write, close)
stat messages can get extended information from a driver
ioctl can set driver parameters
read/write would use the length parameter to specify size of transaction
Almost all drivers fit this model nicely, and extensions are possible through custom messages.
Driverid will likely be renamed DriverClass or DriverType
Thanks! Is your plan that the user's program will scan the mailboxes at startup looking for specific drivers like console I/O, etc using the "driverid" field?
I'm not trying to be a naysayer, just trying to get more details out in the open so we can either get buy-in or see what needs tweaking.
1) With 6 bits for the DRIVERID (I suggest DRIVERTYPE) we are limited to 32 types. That will likely cover the common cases, but there will obviously be more than 32 driver types in use. Would we have a type "other" and then a function call to an DRIVERINFO function be used to get extended info.
2) What is responsible for setting up the mailbox? Does the driver search the bit field mentioned earlier and find the next open slot, or is the application loading the driver responsible for doing that? My thought is that the loader is responsible.
C.W.
Good questions cw! And DRIVERTYPE is a better name, (or DRIVERCLASS) than DRIVERID
Actually 64 types, potentially extended with 128 sub-types using the reserved field.
Yes, a "stat" message could get extended information, written to the buffer pointed to by the TO pointer for example.
For a "bare metal" application, I'd use something like:
SKIP BYTE 0[$E80]
SYS LONG 60_000_000 ' _CLKFREQ
LONG $FFFFFE + _CLKMODE
LONG 0,0
MBX1 LONG _driver_serial << 23 ' set up driver id
LONG 0,0,0
' optionally pad to $1000, where apps load, bare metal need not pad
include "app_main_cog.bin"
include "driver_serial.bin"
The main app would be responsible for launching the driver cog.
The above assumes bare metal, pasm app, obviously it will be a little different for Spin2, propgcc, etc
For non-bare metal, the actual construction of the mailboxes would be the responsibility of the app in whatever language it was written in, under whatever loader.
Basically, using these mailboxes, bare metal apps, PropDos, Sphinx, etc etc etc, could all share drivers, without having to port to each language.
Thanks! Is your plan that the user's program will scan the mailboxes at startup looking for specific drivers like console I/O, etc using the "driverid" field?
I'll show you a couple of examples of how it would work.
Generic SPI Example: (illustrative only, not 100% correct)
' to initialize the driver. "mboxN" refers to the mailbox used for this driver
mboxN long TYPE_SPI<<23+MSG_OPEN ' cogid would be placed in uppermost 3 bits during open
byte spi_cs, spi_clk, spi_di, spi_do
long spi_bitrate ' maybe in processor clocks
long 0
' to write N bytes
mboxN long TYPE_SPI<<23+MSG_WRITE
byte hub_send_ptr ' the from pointer
long 0
long N
' to read N bytes
mboxN long TYPE_SPI<<23+MSG_READ
byte 0
long hub_receive_ptr
long N
' to close spi driver
mboxN long TYPE_SPI<<23+MSG_CLOSE
byte 0
byte 0
Genric PS/2 keyboard Example: (illustrative only, not 100% correct)
' to initialize the driver. "mboxN" refers to the mailbox used for this driver
mboxN long TYPE_KB<<23+MSG_OPEN ' cogid would be placed in uppermost 3 bits during open
byte kb_clk, kb_dat, 0,0
long 0
long 0
' to write N bytes
mboxN long TYPE_KB<<23+MSG_WRITE
byte hub_send_ptr ' the from pointer
long 0
long N
' to read N bytes
mboxN long TYPE_KB<<23+MSG_READ
byte 0
long hub_receive_ptr
long N
' to close spi driver
mboxN long TYPE_KB<<23+MSG_CLOSE
byte 0
byte 0
The above examples are for synchronous (blocking) I/O. For Async I/O, use MSG_ASYNC_READ, MSG_ASYNC_WRITE and poll for completion with MSG_POLL_XMIT_DONE, MSG_POLL_RCVD_CHARS
For a combined mouse/kb driver, the open call could also specify mouse_clk and mouse_dat, and then specify the sub-device (kb, mouse) using the reserved bits of the first long.
For those mailbox formats that have subfields (like the driver type) I think we should as much as possible put those subfields on byte or word boundaries, to reduce the complexity of code that parses them. For example, I think that rather than a 6 bit driverid we should have an 8 bit field. I also don't think there's any need for a cog id field... there's really very little (if anything?) that can be done with that by a client, and if for some reason we do want to discover the cog id it can be done with an ioctl call.
The reason for the current boundaries is due to the prop's MOVI, MOVD and MOVS instructions, and the reason for the cogid is to be able to tell which cog is servicing that driver, in case it needs to be stopped and re-started.
Byte boundaries could be used if we use "movf" - however that is a Prop2 feature, and not available on the P1. Parsing out any field is easy with an SHR and an AND; and constructing them in most cases involves just movi, movd and movs.
In most cases, I'd expect the drivertype and cogid to be set with a WRWORD during the open call; and commands would be set with a WRWORD to the lower word, or even a WRBYTE to byte 1 (with LSB 0)
In most cases, I also expect the FROM/TO/SIZE longs to be written as longs.
On the P2, we will also be able to do a WRQUAD to send a message atomically.
For those mailbox formats that have subfields (like the driver type) I think we should as much as possible put those subfields on byte or word boundaries, to reduce the complexity of code that parses them. For example, I think that rather than a 6 bit driverid we should have an 8 bit field. I also don't think there's any need for a cog id field... there's really very little (if anything?) that can be done with that by a client, and if for some reason we do want to discover the cog id it can be done with an ioctl call.
The reason for the current boundaries is due to the prop's MOVI, MOVD and MOVS instructions, and the reason for the cogid is to be able to tell which cog is servicing that driver, in case it needs to be stopped and re-started.
The thing is, MOVI, MOVD, and MOVS are only useful for setting up the fields, not for parsing them -- and parsing is pretty common. For that. doing a rdbyte is a lot easier than rdlong and then shifts and masks.
Moreover, stopping the driver is probably something that should be done by requesting the driver itself to stop, rather than calling cogstop. That way, if the cog is handling multiple drivers (very possible with P2 multitasking) or is in the middle of an operation it won't be disrupted.
But if we do want the cogid field, how about an 8 bit driverid, then 5 reserved bits (usually 0), 3 bit cog id, then 16 bits for function? That way the driver id field, which is rather important for any service discovery, can easily be read with a RDBYTE (or RDBYTEC on P2). Typical drivers will only use the bottom 9 bits of the function field, and can modify it with MOVS.
The thing is, MOVI, MOVD, and MOVS are only useful for setting up the fields, not for parsing them -- and parsing is pretty common. For that. doing a rdbyte is a lot easier than rdlong and then shifts and masks.
Moreover, stopping the driver is probably something that should be done by requesting the driver itself to stop, rather than calling cogstop. That way, if the cog is handling multiple drivers (very possible with P2 multitasking) or is in the middle of an operation it won't be disrupted.
But if we do want the cogid field, how about an 8 bit driverid, then 5 reserved bits (usually 0), 3 bit cog id, then 16 bits for function? That way the driver id field, which is rather important for any service discovery, can easily be read with a RDBYTE (or RDBYTEC on P2). Typical drivers will only use the bottom 9 bits of the function field, and can modify it with MOVS.
I'll show you a couple of examples of how it would work.
Generic SPI Example: (illustrative only, not 100% correct)
' to initialize the driver. "mboxN" refers to the mailbox used for this driver
mboxN long TYPE_SPI<<23+MSG_OPEN ' cogid would be placed in uppermost 3 bits during open
byte spi_cs, spi_clk, spi_di, spi_do
long spi_bitrate ' maybe in processor clocks
long 0
' to write N bytes
mboxN long TYPE_SPI<<23+MSG_WRITE
byte hub_send_ptr ' the from pointer
long 0
long N
' to read N bytes
mboxN long TYPE_SPI<<23+MSG_READ
byte 0
long hub_receive_ptr
long N
' to close spi driver
mboxN long TYPE_SPI<<23+MSG_CLOSE
byte 0
byte 0
Genric PS/2 keyboard Example: (illustrative only, not 100% correct)
' to initialize the driver. "mboxN" refers to the mailbox used for this driver
mboxN long TYPE_KB<<23+MSG_OPEN ' cogid would be placed in uppermost 3 bits during open
byte kb_clk, kb_dat, 0,0
long 0
long 0
' to write N bytes
mboxN long TYPE_KB<<23+MSG_WRITE
byte hub_send_ptr ' the from pointer
long 0
long N
' to read N bytes
mboxN long TYPE_KB<<23+MSG_READ
byte 0
long hub_receive_ptr
long N
' to close spi driver
mboxN long TYPE_KB<<23+MSG_CLOSE
byte 0
byte 0
The above examples are for synchronous (blocking) I/O. For Async I/O, use MSG_ASYNC_READ, MSG_ASYNC_WRITE and poll for completion with MSG_POLL_XMIT_DONE, MSG_POLL_RCVD_CHARS
For a combined mouse/kb driver, the open call could also specify mouse_clk and mouse_dat, and then specify the sub-device (kb, mouse) using the reserved bits of the first long.
Bill,
I think that the second long, which sometimes you defined as 4 bytes, other times you defined as
byte hub_send_ptr ' the from pointer
But should be
long hub_send_ptr ' the from pointer
I think the cogid would better be served to be in the 5 icccc bits (b22..18) since they would only be set once. Perhaps format ttccc where tt=taskid (or 00) and ccc =cogid. Later, if we get a prop with >8 cogs, we can use the tt field. Therefore put them where it is more difficult to get at.
I also agree that accessing them by byte or word makes more sense and easier. Lets work on this some more.
BTW not getting much time over this w/e with family etc.
Just an update to bring this thread back up since it was asked elsewhere.
RossH is recovering from a lengthy illness so is not able to give any input until after April.
Bill & I have agreed that we will use a quad long for each mailbox, and they will start at $e80 and go to $FFF. The first is used as clkfreq, etc. It may be that the second is also required for system parameters. This is unimportant, just that they are quad longs. And opposite to Bill's take, the quad is because of the binary nature - shift 2! 1/3/5/6/7 just don't work. Ross uses 1 then 2 but the overheads make this seem (in hindsight) better for single 4 long allocations. 4 longs gives us plenty of options.
I think that it is at least fairly universally agreed that user code will start at hub $01000. However, for bare metal users, they can do what they like, including starting at $e80 if that is what suits them. All they lose is being able to use (our/my/?) standard drivers and code without modification.
With the work I have been doing on the LMM debugger, I expect I will write a module to do the allocation and locating in LMM that can be called by anyone's program/driver to allocate/use one of the mailboxes.
For now I see 2 standard variants...
* character interface
* pointer interface to buffer(s) - maybe ring buffer, maybe string buffer.
Obviously there would be custom ones as well.
There is sufficient info in this thread to get a grasp on what the mailboxes are about.
I think it fair to say, Bill & I are getting on with implementing it. Anyone is welcome to jump aboard with us. If it does not suit you, that's fine too.
Comments
Yes, four longs due to the quad instructions, plus easy fast calculation of mailbox address.
The market will decide anyway - between simple easy to use mailboxes, or many variations, each unique.
I really don't understand the objections to a simple format. If more data is needed, there are two pointers.
My work will follow this mailbox format; it sounds like a lot of Ray's will as well - perhaps others will use it as well.
For bare metal code, there is no need for a global header - as the whole image will likely be statically linked.
I'm not trying to be a naysayer, just trying to get more details out in the open so we can either get buy-in or see what needs tweaking.
1) With 6 bits for the DRIVERID (I suggest DRIVERTYPE) we are limited to 32 types. That will likely cover the common cases, but there will obviously be more than 32 driver types in use. Would we have a type "other" and then a function call to an DRIVERINFO function be used to get extended info.
2) What is responsible for setting up the mailbox? Does the driver search the bit field mentioned earlier and find the next open slot, or is the application loading the driver responsible for doing that? My thought is that the loader is responsible.
C.W.
"generic" mailbox format
long 0 - function/status
long 1 - from pointer
long 2 - to pointer
long 3 - length/size
current thinking on long 0 encoding:
[cogid:3, driverid:6, reserved:7][status:7,command/data:9]
More info on mailbox:
High word in long:
cogid - ID of cog running the driver associated with this mailbox
driverid - type of driver (serial, i2c, spi, vga, hdtv, ntsc, pal, etc - more like class of driver) - allows 64 different types of drivers, reserved could be used to extend this
reserved - possibly later used to give info like number of serial ports, etc
Low word in long:
7 bits of status - command completed OK, command error, char available etc
9 bits of data - normally only 8 used, but 9 bits useful to pass "null" byte for drivers polling word for 0 value
The following three longs, whenever reasonable, would be the pointers to/from the data, and size of data; however specific drivers can use them differently if the driver does not fit a simplified unix-like driver model (open, stat, ioctl, read, write, close)
stat messages can get extended information from a driver
ioctl can set driver parameters
read/write would use the length parameter to specify size of transaction
Almost all drivers fit this model nicely, and extensions are possible through custom messages.
Driverid will likely be renamed DriverClass or DriverType
Good questions cw! And DRIVERTYPE is a better name, (or DRIVERCLASS) than DRIVERID
Actually 64 types, potentially extended with 128 sub-types using the reserved field.
Examples:
DriverClass: SPI
reserved/Instance/SubClass: MCP3208
DriverClass: VGA
SubClass: XGA
DriverClass: Serial
SubClass: 4 port
Yes, a "stat" message could get extended information, written to the buffer pointed to by the TO pointer for example.
For a "bare metal" application, I'd use something like:
The main app would be responsible for launching the driver cog.
The above assumes bare metal, pasm app, obviously it will be a little different for Spin2, propgcc, etc
For non-bare metal, the actual construction of the mailboxes would be the responsibility of the app in whatever language it was written in, under whatever loader.
Basically, using these mailboxes, bare metal apps, PropDos, Sphinx, etc etc etc, could all share drivers, without having to port to each language.
See my reply to cw.
Bare metal could statically link it in, loaders could dynamically load.
PropGCC examples:
Statically linked driver:
ctr0.s sets up the mailbox, linker inserts driver
Dynamically linked example:
crt0.s sets up mailbox,
in main we have something like:
ok = _loaddriver(0,"serial.bin"); // we don't even care where it loads!
or
char cog_load_area[2048];
ok = loaddriver(0,cog_load_area,"serial.bin");
of course a different version could return the mailbox handle, instead of specifying it
This also allows replacing drivers at run-time
Regarding your question, an application could scan to see if the driver is already loaded, and if not, load it.
For bare metal, i expect drivers to be statically linked.
Of course it would also be possible to statically link a default driver, which would be replaced at run time.
Many interesting possibilities
I Made my HEAD file and found that MBOX structure have no place for particular PIN's used by driver
I'll show you a couple of examples of how it would work.
Generic SPI Example: (illustrative only, not 100% correct)
Genric PS/2 keyboard Example: (illustrative only, not 100% correct)
The above examples are for synchronous (blocking) I/O. For Async I/O, use MSG_ASYNC_READ, MSG_ASYNC_WRITE and poll for completion with MSG_POLL_XMIT_DONE, MSG_POLL_RCVD_CHARS
For a combined mouse/kb driver, the open call could also specify mouse_clk and mouse_dat, and then specify the sub-device (kb, mouse) using the reserved bits of the first long.
Thanks.
But I see that still need much thinking
Eric
The reason for the current boundaries is due to the prop's MOVI, MOVD and MOVS instructions, and the reason for the cogid is to be able to tell which cog is servicing that driver, in case it needs to be stopped and re-started.
Byte boundaries could be used if we use "movf" - however that is a Prop2 feature, and not available on the P1. Parsing out any field is easy with an SHR and an AND; and constructing them in most cases involves just movi, movd and movs.
In most cases, I'd expect the drivertype and cogid to be set with a WRWORD during the open call; and commands would be set with a WRWORD to the lower word, or even a WRBYTE to byte 1 (with LSB 0)
In most cases, I also expect the FROM/TO/SIZE longs to be written as longs.
On the P2, we will also be able to do a WRQUAD to send a message atomically.
The thing is, MOVI, MOVD, and MOVS are only useful for setting up the fields, not for parsing them -- and parsing is pretty common. For that. doing a rdbyte is a lot easier than rdlong and then shifts and masks.
Moreover, stopping the driver is probably something that should be done by requesting the driver itself to stop, rather than calling cogstop. That way, if the cog is handling multiple drivers (very possible with P2 multitasking) or is in the middle of an operation it won't be disrupted.
But if we do want the cogid field, how about an 8 bit driverid, then 5 reserved bits (usually 0), 3 bit cog id, then 16 bits for function? That way the driver id field, which is rather important for any service discovery, can easily be read with a RDBYTE (or RDBYTEC on P2). Typical drivers will only use the bottom 9 bits of the function field, and can modify it with MOVS.
Eric
High word:
[drivertype:8][reserved:5][cogid:3]
Low word:
[status:7][command:9]
I think that the second long, which sometimes you defined as 4 bytes, other times you defined as But should be I think the cogid would better be served to be in the 5 icccc bits (b22..18) since they would only be set once. Perhaps format ttccc where tt=taskid (or 00) and ccc =cogid. Later, if we get a prop with >8 cogs, we can use the tt field. Therefore put them where it is more difficult to get at.
I also agree that accessing them by byte or word makes more sense and easier. Lets work on this some more.
BTW not getting much time over this w/e with family etc.
RossH is recovering from a lengthy illness so is not able to give any input until after April.
Bill & I have agreed that we will use a quad long for each mailbox, and they will start at $e80 and go to $FFF. The first is used as clkfreq, etc. It may be that the second is also required for system parameters. This is unimportant, just that they are quad longs. And opposite to Bill's take, the quad is because of the binary nature - shift 2! 1/3/5/6/7 just don't work. Ross uses 1 then 2 but the overheads make this seem (in hindsight) better for single 4 long allocations. 4 longs gives us plenty of options.
I think that it is at least fairly universally agreed that user code will start at hub $01000. However, for bare metal users, they can do what they like, including starting at $e80 if that is what suits them. All they lose is being able to use (our/my/?) standard drivers and code without modification.
With the work I have been doing on the LMM debugger, I expect I will write a module to do the allocation and locating in LMM that can be called by anyone's program/driver to allocate/use one of the mailboxes.
For now I see 2 standard variants...
* character interface
* pointer interface to buffer(s) - maybe ring buffer, maybe string buffer.
Obviously there would be custom ones as well.
There is sufficient info in this thread to get a grasp on what the mailboxes are about.
I think it fair to say, Bill & I are getting on with implementing it. Anyone is welcome to jump aboard with us. If it does not suit you, that's fine too.