(no commit message)
[libreriscv.git] / isa_conflict_resolution / ioctl.mdwn
1 # pluggable extensions
2
3 ==RB===
4
5 This proposal adds a standardised extension instructions to the RV
6 instruction set by introducing a fixed small number N (e.g. N = 8) of
7 R-type opcodes xcmd0 rd, rs1, rs2, .. , xcmd7 rd, rs1, rs2.
8 The input value in rs2 is arbitrary.
9 The content of the first input rs1, however, is divided in a 12bit "logical unit" (lun) together with xlen - 12 bits of additional data.
10 The lun bits in rs1, determines a specific (sub)device, and the CPU routes the command to this device with rs1 and rs2 as input, and rd as output. Effectively, the xcmd0, ... xcmd7 instructions are "virtual method" opcodes, overloaded for different extension (sub)devices.
11
12
13 The specific value of the lun is supposed to be convenient for the cpu and is thus unstandardised. Portable software therefore constructs the lun, with a further R-type instruction xext. It takes a 20 bit universally unique identifier (UUID) that identifies an interface with upto N R-type instructions with the signature of xcmd. 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. , but implemented by the extension (sub)device. Note that this scheme gives an easy work around the restriction on N (e.g. 8 ) commands: an implementing device can simply implement several interfaces as routable subdevices, indeed is expected to do so.
14
15 The net effect is that a sequence like
16
17 //fake UUID
18 lui rd 0xEDCBA
19 xext rd rd rs1
20 xcmd0 rd rd rs2
21
22 acts like a single namespaced instruction EDCBA_cmd0 rd rs1 rs2 with the annoying caveat that rs1 can only use bits 0..XLEN-12 (the sequence is not indivisible but the crucial semantics that you might want to be indivisible in xcmd0).
23
24 Programatically the instructions in the interface are just a set of glorified assembler macros
25
26 org.tinker.tinker:RocknRoll{
27 uuid : 0xABCDE
28 rock rd rs1 rs2 : xcmd0 rd rs1 rs2
29 roll rd rs1 rs2 : xcmd1 rd rs1 rs2
30 }
31
32 (or perhaps just non glorified standard assembler macros or defines with long names e.g.)
33
34 #define org_tinker_tinker__RocknRoll__interface_uuid 0xABCDE
35 #define org_tinker_tinker__RocknRoll__rock(rd, rs1, rs2) xcmd0 rd, rs1, rs2
36 #define org_tinker_tinker__RocknRoll__roll(rd, rs1, rs2) xcmd1 rd, rs1, rs2
37
38 so the same sequence is more readable as
39
40 lui rd org.tinker.tinker__RocknRoll__interface_uuid
41 xext rd rs1
42 org_tinker_tinker__RocknRoll__rock(rd, rd, rs2)
43
44
45 If several instructions of the same interface are used one can also have code like
46 lui t1 org_tinker_tinker__RocknRoll__interface_uuid
47
48 xext t1 zero
49 xcmd0 a5, t1, a0 // org_tinker_tinker__RocknRoll__rock(a5, t1, a0)
50 xcmd1 t2, t1, a1 // org_tinker_tinker__RocknRoll__roll(t2, t1, a5)
51 xcmd0 a0, t1, t2 // org_tinker_tinker__RocknRoll__rock(a0, t1, t2)
52
53 This amortises the cost of the xext instruction.
54
55 ==Implications for the RiscV ecosystem ==
56
57
58 The proposal thus allow independent g to define one or more extensan 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.
59
60
61 Having a standardised overloadable interface simply avoids much of the
62 need for isa extensions for hardware with non standard interfaces and
63 semantics. This is analogous to the way that the standardised overloadable
64 ioctl interface of the kernel almost completely avoids the need for
65 extending the kernel with syscalls for the myriad of hardware devices
66 with their specific interfaces and semantics.
67
68 Since the rs1 input of the overloaded ext_ctl instruction's are taken
69 by the interface cookie, they are restricted in use compared to a normal
70 R-type instruction (it is possible to pass 12 bits of additional info by
71 or ing it with the cookie). Delegation is also expected to come at a small
72 additional performance price compared to a "native" instruction. This
73 should be an acceptable tradeoff in most cases.
74
75 The expanded flexibility comes at the cost: the standard can specify the
76 semantics of the delegation mechanism and the interfacing with the rest
77 of the cpu, but the actual semantics of the overloaded instructions can
78 only be defined by the designer of the interface. Likewise, a device
79 can be conforming as far as delegation and interaction with the CPU
80 is concerned, but whether the hardware is conforming to the semantics
81 of the interface is outside the scope of spec. Being able to specify
82 that semantics using the methods used for RV itself is clearly very
83 valuable. One impetus for doing that is using it for purposes of its own,
84 effectively freeing opcode space for other purposes. Also, some interfaces
85 may become de facto or de jure standards themselves, necessitating
86 hardware to implement competing interfaces. I.e., facilitating a free
87 for all, may lead to standards proliferation. C'est la vie.
88
89 The only "ISA-collisions" that can still occur are in the 20 bit (~10^6)
90 interface identifier space, with 12 more bits to identify a device on
91 a hart that implements the interface. One suggestion is setting aside
92 2^19 id's that are handed out for a small fee by a central (automated)
93 registration (making sure the space is not just claimed), while the
94 remaining 2^19 are used as a good hash on a long, plausibly globally
95 unique human readable interface name. This gives implementors the choice
96 between a guaranteed private identifier paying a fee, or relying on low
97 probabilities. On RV64 the UUID can also be extended to 52 bits (> 10^15).
98
99
100 ==== Description of the extension as C functions.==
101
102 /* register format of rs1 for xext instructions */
103 typedef struct uuid_device{
104 long dev:12;
105 long uuid: 8*sizeof(long) - 12;
106 } uuid_device_t
107
108 /* register format for rd of xext and rs1 for xcmd instructions, packs lun and data */
109 typedef struct lun_data{
110 long lun:12;
111 long data: 8*sizeof(long) - 12;
112 } lun_data_t
113
114 /* proposed R-type instructions
115 xext rd rs1 rs2
116 xcmd0 rd rs1 rs2
117 xcmd1 rd rs1 rs2
118 ...
119 xcmd7 rd rs1 rs2
120 */
121
122 lun_data_t xext(uuid_dev_t rs1, long rs2);
123 long xcmd0(lun_data_t rs1, long rs2);
124 long xcmd1(lun_data_t rs1, long rs2);
125 ...
126 long xcmd<N>(lun_data_t rs1, long rs2);
127
128 /* hardware interface presented by an implementing device. */
129 typedef
130 long device_fn(unsigned short subdevice_xcmd, lun_data_t rs1, long rs2);
131
132 /* cpu internal datatypes */
133
134 typedef
135 struct lun{
136 unsigned short id:12
137 } lun_t;
138
139 struct uuid_device2lun{
140 uuid_dev_t uuid_dev;
141 lun_t lun;
142 };
143
144 struct device_subdevice{
145 device_fn* device_addr;
146 unsigned short subdeviceId:12;
147 };
148
149 struct lun2device_subdevice{
150 lun_t lun;
151 struct device_subdevice devAddr_subdevId;
152 }
153
154 struct uuid_dev2lun lun_map[];
155
156 /* associative memory magic to map UUID + device to a convenient 12 bit lun, returns (lun_t){0} on failure */
157
158 lun_t cpu_lookup_lun(const struct uuid_dev2device_subdevice* lun_map, uuid_dev_t uuid_dev);
159
160 lun_data_t xext(uuid_dev_t rs1, long rs2)
161 {
162 lun_t lun = cpu_lookup_lun(lun_map, rs1);
163
164 return (lun_data_t){.lun = lun.id, .data = rs2 % (1<< (8*sizeof(long) - 12))}
165 }
166
167 struct lun2device_subdevice device_subdevice_map[];
168
169 /* maps lun to struct device_subdevice pair. In particular for lun = 0, returns (struct device_subdevice){NULL,0} on failure, */
170 device_subdevice_t cpu_lookup_device_subdevice(const struct lun2device_subdevice_map* dev_subdevice_map, short lun);
171
172 /* functional description of the delegating xcmd0 .. xcmd7 instructions */
173 template<k = 0..N-1> //pretend this is C
174 long xcmd<k>(lun_data_t rs1, long rs2)
175 {
176 struct device_subdevice dev_subdev = cpu_lookup_device_subdevice(device_subdevice_map, rs1.lun);
177 if(dev_subdev.devAddr == NULL)
178 trap(“Illegal instruction”);
179
180 return dev_subdev.devAddr(dev_subdev.subdevId | k << 12, rs1, rs2);
181 }
182
183
184
185 Example:
186
187 #define COM_BIGBUCKS__FROBATE__INTERFACE_UUID 0xABCDE
188 #define ORG_TINKER_TINKER__ROCKNROLL_INTERFACE_UUID 0x12345
189 #define ORG_TINKER_TINKER__JAZZ_INTERFACE_UUID 0xD0B0D
190
191 com.bigbucks:Frobate{
192 uuid: COM_BIGBUCKS__FROBATE__INTERFACE_UUID
193 frobate rd rs1 rs2 : cmd0 rd rs1 rs2
194 foo rd rs1 rs2 : cmd1 rd rs1 rs2
195 bar rd rs1 rs2 : cmd1 rd rs1 rs2
196 }
197
198 org.tinker.tinker:RocknRoll{
199 uuid: ORG_TINKER_TINKER__ROCKNROLL_INTERFACE_UUID
200 rock rd rs1 rs2: cmd0 rd rs1 rs2
201 roll rd rs1 rs2: cmd1 rd rs1 rs2
202 }
203
204 long com_bigbucks__device1(short subdevice_xcmd, lun_data_t rs1, long rs2)
205 {
206 switch(subdevice_xcmd) {
207 case 0 | 0 << 12 /* com.bigbucks:Frobate:frobate */ : return device1_frobate(rs1, rs2);
208 case 0 | 1 << 12 /* com.bigbucks:Frobate:foo */ : return device1_foo(rs1, rs2);
209 case 0 | 2 << 12 /* com.bigbucks:Frobate:bar */ : return device1_bar(rs1, rs2);
210 case 1 | 0 << 12 /* org.tinker.tinker:RocknRoll:rock */ : return device1_rock(rs1, rs2);
211 case 1 | 1 << 12 /* org.tinker.tinker:RocknRoll:roll */ : return device1_roll(rs1, rs2);
212 default: trap(“hardware configuration error”);
213 }
214 }
215
216 org.tinker.tinker:Jazz{
217 uuid: ORG_TINKER_TINKER__JAZZ_INTERFACE_UUID
218 boogy rd rs1 rs2: cmd0 rd rs1 rs2
219 }
220
221 long org_tinker_tinker__device2(short subdevice_xcmd, lun_data_t rs1, long rs2)
222 {
223 switch(dev_cmd.interfId){
224 case 0 | 0 << 12 /* com.bigbucks:Frobate:frobate */: return device2_frobate(rs1, rs2);
225 case 0 | 1 << 12 /* com.bigbucks:Frobate:foo */ : return device2_foo(rs1, rs2);
226 case 0 | 2 << 12 /* com.bigbucks:Frobate:bar */ : return device2_foo(rs1, rs2);
227 case 1 | 0 << 12 /* org_tinker_tinker:Jazz:boogy */: return device2_boogy(rs1, rs2);
228 default: trap(“hardware configuration error”);
229 }
230 }
231
232 /* struct lun2dev_subdevice_map[] */
233 dev_subdevice_map = {
234 // .lun = 0, error and falls back to trapping xcmd
235 {.lun = 1, .devAddr_interfId = {fallback, 0 /* ReturnZero */}},
236 {.lun = 2, .devAddr_interfId = {fallback, 1 /* ReturnMinusOne*/}},
237 // .lun = 3 .. 7 reserved for other fallback RV interfaces
238 // .lun = 8 .. 30 reserved as error numbers, c.li t1 31; bltu rd t1 L_fail tests errors
239 // .lun = 31 reserved out of caution
240 {.lun = 32, .devAddr_interfId = {device1, 0 /* Frobate interface */}},
241 {.lun = 33, .devAddr_InterfId = {device1, 1 /* RocknRoll interface */}},
242 {.lun = 34, .devAddr_interfId = {device2, 0 /* Frobate interface */}},
243 {.lun = 35, .devAddr_interfId = {device2, 1 /* Jazz interface */}},
244 }
245
246
247 /* struct uuid_dev2lun_map[] */
248 lun_map = {
249 {.uuid_devId = {ORG_RISCV__FALLBACK__RETURN_ZERO__INTERFACE_UUID , 0}, .lun = 1},
250 {.uuid_devId = {ORG_RISCV__FALLBACK__RETURN_MINUSONE__INTERFACE_UUID, 0},.lun = 2},
251 {.uuid_devId = {COM_BIGBUCKS__FROBATE__INTERFACE_UUID, 0}, .lun = 32},
252 {.uuid_devId = {COM_BIGBUCKS__FROBATE__INTERFACE_UUID, 1}, .lun = 34}, //sic!
253 {.uuid_devId = {ORG_TINKER_TINKER__ROCKNROLL__INTERFACE_UUID, 0}, .lun = 33}, //sic!
254 {.uuid_devId = {ORG_TINKER_TINKER__JAZZ__INTERFACE_UUID, 0}, .lun = 35}
255 }