update
[libreriscv.git] / isa_conflict_resolution / ioctl.mdwn
index dac39dca48e81ad4d4530db95645dd784eda5281..e7ea56d0680b076282e3414ff80433931fe61ab8 100644 (file)
@@ -1,250 +1,2 @@
-# pluggable extensions 
+The ioctls proposal was a precursor of the [[overloadable opcodes]] proposal. Please see there.
 
-==RB===
-
-This proposal adds a standardised extension instructions to the RV
-instruction set by introducing a fixed small number N (e.g. N = 8) of
-R-type opcodes xcmd0, .. xcmd7. Each takes in rs1 a 12bit "logical unit" (lun)
-identifying a (on or off chip) (sub)device with at most N commands that can execute the command, together with xlen - 12 bits of additional data. Based on the logical unit bits in rs1 the CPU routes each of the 8 commands to a  specific interface on a specific (sub)device on the CPU. Effectively, the xcmd0, ... xcmd7 instructions become "virtual method" opcodes, that can be overloaded for different extension (sub)devices
-
-The specific value of the lun is supposed to be convenient for the cpu to route the xcmd to the proper device and thus unstandardised. To portably construct the lun a further R-type instruction we define xext. It takes a 20 bit universally unique identifier identifying an interface with upto 8 almost (but not quite) standard R-type instructions, but implemented by the extension (sub)device. The restriction on 8 command commands is not problematic because a hardware implementation can (indeed is expected) to implement several interfaces as a subdevice. An optional sequence number identifies a specific enumerated device on the cpu that implements the interface as a subdevice. For convenience, xext also or's bits rs2[0..XLEN-12]. If the UUID is not recognised 0 is returned.   
-
-Remark: xext is a purely stateless translation (and packing) operaton (unlike previous proposals). 
-
-The proposal allows people to define an extension interface of 8 (slightly crippled) R-type instructions implemented by an extension device, (e.g. an IP tile) configured at manufacturing or even startup time of the CPU. A sequence like 
-
-//fake UUID
-lui   rd 0xEDCBA
-xext  rd rd rs1
-xcmd0 rd rd rs2 
-
-then acts like a single instruction EDCBA_cmd0 rd rs1 rs2 with the caveats that, annoyingly, rs1 can only use bits 0..XLEN-12 (the sequence is not indivisible but the crucial semantics that you might want indivisible is in xcmd0). This can be used almost exactly like an R-type instruction rock rd, rs1, rs2 (in fact can be opcode fused if so wished). 
-
-Programatically the instructions in the interface are just a set of glorified assembler macros
-
-org.tinker.tinker:RocknRoll{
-     uuid : 0xABCDE
-     rock rd rs1 rs2 : xcmd0 rd rs1 rs2
-     roll rd rs1 rs2 : xcmd1 rd rs1 rs2
-}
-
-(or perhaps just non glorified standard assembler macros or defines with long names e.g.)
-
-  #define org_tinker_tinker__RocknRoll__interface_uuid 0xABCDE 
-  #define org_tinker_tinker__RocknRoll__rock(rd, rs1, rs2) xcmd0 rd, rs1, rs2
-  #define org_tinker_tinker__RocknRoll__roll(rd, rs1, rs2) xcmd1 rd, rs1, rs2
-
-so the same sequence is more readable as
-
-   lui   rd org.tinker.tinker__RocknRoll__interface_uuid 
-   xext  rd rs1
-   org_tinker_tinker__RocknRoll__rock(rd, rd, rs2)
-
-
-If several instructions of the same interface are used one can also have code like 
-lui   t1 org_tinker_tinker__RocknRoll__interface_uuid
-
-   xext  t1 zero
-   xcmd0 a5, t1, a0  // org_tinker_tinker__RocknRoll__rock(a5, t1, a0) 
-   xcmd1 t2, t1, a1  // org_tinker_tinker__RocknRoll__roll(t2, t1, a5)
-   xcmd0 a0, t1, t2  // org_tinker_tinker__RocknRoll__rock(a0, t1, t2)
-
-This amortises the cost of the xext instruction. 
-
-==Implications for the RiscV ecosystem ==
-
-Having a standardised overloadable interface simply avoids much of the
-need for isa extensions for hardware with non standard interfaces and
-semantics. This is analogous to the way that the standardised overloadable
-ioctl interface of the kernel almost completely avoids the need for
-extending the kernel with syscalls for the myriad of hardware devices
-with their specific interfaces and semantics.
-
-Since the rs1 input of the overloaded  ext_ctl instruction's are taken
-by the interface cookie, they are restricted in use compared to a normal
-R-type instruction (it is possible to pass 12 bits of additional info by
-or ing it with the cookie). Delegation is also expected to come at a small
-additional performance price compared to a "native" instruction. This
-should be an acceptable tradeoff in most cases.
-
-The expanded flexibility comes at the cost: the standard can specify the
-semantics of the delegation mechanism and the interfacing with the rest
-of the cpu, but the actual semantics of the overloaded instructions can
-only be defined by the designer of the interface. Likewise, a device
-can be conforming as far as delegation and interaction with the CPU
-is concerned, but whether the hardware is conforming to the semantics
-of the interface is outside the scope of spec. Being able to specify
-that semantics using the methods used for RV itself is clearly very
-valuable. One impetus for doing that is using it for purposes of its own,
-effectively freeing opcode space for other purposes. Also, some interfaces
-may become de facto or de jure standards themselves, necessitating
-hardware to implement competing interfaces. I.e., facilitating a free
-for all, may lead to standards proliferation. C'est la vie.
-
-The only "ISA-collisions" that can still occur are in the 20 bit (~10^6)
-interface identifier space, with 12 more bits to identify a device on
-a hart that implements the interface. One suggestion is setting aside
-2^19 id's that are handed out for a small fee by a central (automated)
-registration (making sure the space is not just claimed), while the
-remaining 2^19 are used as a good hash on a long, plausibly globally
-unique human readable interface name. This gives implementors the choice
-between a guaranteed private identifier paying a fee, or relying on low
-probabilities. On RV64 the UUID can also be extended to 52 bits (> 10^15).
-
-
-==== Description of the extension as C functions.== 
-
-/* register format of rs1 for xext instructions */
-typedef struct uuid_device{
-      long dev:12;
-      long uuid: 8*sizeof(long) - 12;
-} uuid_device_t
-
-/* register format for rd of xext and rs1  for xcmd instructions, packs lun and data */
-typedef struct lun_data{
-      long lun:12;
-      long data: 8*sizeof(long) - 12;
-} lun_data_t
-
-/* proposed R-type instructions 
-   xext  rd rs1 rs2
-   xcmd0 rd rs1 rs2
-   xcmd1 rd rs1 rs2
-   ...
-   xcmd7 rd rs1 rs2
-*/
-
-lun_data_t xext(uuid_dev_t rs1, long rs2);
-long xcmd0(lun_data_t rs1, long rs2);
-long xcmd1(lun_data_t rs1, long rs2);
-...
-long xcmd<N>(lun_data_t rs1, long rs2);
-
-/* hardware interface presented by an implementing device. */
-typedef
-long device_fn(unsigned short subdevice_xcmd, lun_data_t rs1, long rs2);
-
-/* cpu internal datatypes */
-
-typedef 
-struct lun{
-      unsigned short id:12
-} lun_t;
-
-struct uuid_device2lun{
-      uuid_dev_t    uuid_dev;
-      lun_t lun;
-};
-
-struct device_subdevice{
-      device_fn* device_addr;
-      unsigned short subdeviceId:12;
-};      
-
-struct lun2device_subdevice{
-      lun_t lun;
-      struct device_subdevice devAddr_subdevId;
-}
-
-struct uuid_dev2lun lun_map[];
-
-/* associative memory magic to map UUID + device to a convenient 12 bit lun, returns (lun_t){0} on failure */
-
-lun_t cpu_lookup_lun(const struct uuid_dev2device_subdevice* lun_map, uuid_dev_t uuid_dev);
-
-lun_data_t xext(uuid_dev_t rs1, long rs2)
-{
-     lun_t lun = cpu_lookup_lun(lun_map, rs1);
-
-     return (lun_data_t){.lun = lun.id, .data = rs2 % (1<< (8*sizeof(long) - 12))}
-}
-
-struct lun2device_subdevice device_subdevice_map[];
-
-/* maps lun to struct device_subdevice pair. In particular for lun = 0, returns (struct device_subdevice){NULL,0} on failure,  */
-device_subdevice_t cpu_lookup_device_subdevice(const struct lun2device_subdevice_map* dev_subdevice_map, short lun);
-
-/* functional description of the delegating xcmd0 .. xcmd7 instructions */
-template<k = 0..N-1> //pretend this is C
-long xcmd<k>(lun_data_t rs1, long rs2)
-{
-     struct device_subdevice dev_subdev = cpu_lookup_device_subdevice(device_subdevice_map, rs1.lun);
-     if(dev_subdev.devAddr == NULL)
-          trap(“Illegal instruction”);
-     
-    return dev_subdev.devAddr(dev_subdev.subdevId | k << 12, rs1, rs2);
-}
-
-
-
-Example:
-#define COM_BIGBUCKS__FROBATE__INTERFACE_UUID 0xABCDE
-#define ORG_TINKER_TINKER__ROCKNROLL_INTERFACE_UUID 0x12345
-#define ORG_TINKER_TINKER__JAZZ_INTERFACE_UUID 0xD0B0D
-
-com.bigbucks:Frobate{
-    uuid: COM_BIGBUCKS__FROBATE__INTERFACE_UUID
-    frobate rd rs1 rs2 : cmd0 rd rs1 rs2
-    foo     rd rs1 rs2 : cmd1 rd rs1 rs2
-    bar     rd rs1 rs2 : cmd1 rd rs1 rs2
-}
-
-org.tinker.tinker:RocknRoll{
-    uuid: ORG_TINKER_TINKER__ROCKNROLL_INTERFACE_UUID
-    rock rd rs1 rs2: cmd0 rd rs1 rs2
-    roll rd rs1 rs2: cmd1 rd rs1 rs2
-}
-
-long com_bigbucks__device1(short  subdevice_xcmd, lun_data_t rs1, long rs2)
-{
-      switch(subdevice_xcmd) {
-      case 0 | 0 << 12  /* com.bigbucks:Frobate:frobate */     : return device1_frobate(rs1, rs2);
-      case 0 | 1 << 12  /* com.bigbucks:Frobate:foo */         : return device1_foo(rs1, rs2);
-      case 0 | 2 << 12  /* com.bigbucks:Frobate:bar */         : return device1_bar(rs1, rs2);
-      case 1 | 0 << 12  /* org.tinker.tinker:RocknRoll:rock */ : return device1_rock(rs1, rs2);
-      case 1 | 1 << 12  /* org.tinker.tinker:RocknRoll:roll */ : return device1_roll(rs1, rs2);
-      default: trap(“hardware configuration error”);
-      }
-}
-
-org.tinker.tinker:Jazz{
-    uuid: ORG_TINKER_TINKER__JAZZ_INTERFACE_UUID 
-    boogy rd rs1 rs2: cmd0 rd rs1 rs2
-}
-
-long org_tinker_tinker__device2(short subdevice_xcmd,  lun_data_t rs1, long rs2)
-{
-      switch(dev_cmd.interfId){
-      case 0  | 0 << 12 /* com.bigbucks:Frobate:frobate */: return device2_frobate(rs1, rs2);
-      case 0  | 1 << 12 /* com.bigbucks:Frobate:foo */    : return device2_foo(rs1, rs2);
-      case 0  | 2 << 12 /* com.bigbucks:Frobate:bar */    : return device2_foo(rs1, rs2);
-      case 1  | 0 << 12 /* org_tinker_tinker:Jazz:boogy */: return device2_boogy(rs1, rs2);
-      default: trap(“hardware configuration error”);      
-      }
-}
-
-/* struct lun2dev_subdevice_map[] */
-  dev_subdevice_map = {
-//      .lun = 0,  error and falls back to trapping xcmd 
-       {.lun = 1, .devAddr_interfId = {fallback,    0 /* ReturnZero  */}},
-       {.lun = 2, .devAddr_interfId = {fallback,    1 /* ReturnMinusOne*/}},
-//      .lun = 3 .. 7  reserved for other fallback RV interfaces
-//      .lun = 8 .. 30 reserved as error numbers, c.li t1 31; bltu rd t1 L_fail tests errors
-//      .lun = 31  reserved out of caution 
-       {.lun = 32, .devAddr_interfId = {device1, 0 /* Frobate  interface */}},
-       {.lun = 33, .devAddr_InterfId = {device1, 1 /* RocknRoll interface */}},
-       {.lun = 34, .devAddr_interfId = {device2, 0 /* Frobate interface */}},
-       {.lun = 35, .devAddr_interfId = {device2, 1 /* Jazz interface */}},
-  }
-
-
-/* struct uuid_dev2lun_map[] */  
- lun_map = {     
-     {.uuid_devId = {ORG_RISCV__FALLBACK__RETURN_ZERO__INTERFACE_UUID , 0},  .lun = 1},   
-     {.uuid_devId = {ORG_RISCV__FALLBACK__RETURN_MINUSONE__INTERFACE_UUID, 0},.lun = 2},
-     {.uuid_devId = {COM_BIGBUCKS__FROBATE__INTERFACE_UUID, 0}, .lun = 32},
-     {.uuid_devId = {COM_BIGBUCKS__FROBATE__INTERFACE_UUID, 1}, .lun = 34},        //sic!
-     {.uuid_devId = {ORG_TINKER_TINKER__ROCKNROLL__INTERFACE_UUID, 0}, .lun = 33},  //sic!
-     {.uuid_devId = {ORG_TINKER_TINKER__JAZZ__INTERFACE_UUID, 0}, .lun = 35}
- }