2 Copyright (c) 2013, IIT Madras
5 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 * Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
15 //1. change CPAR, CMAR and CNDTR registers to conditionalWrite registers so that a write to these registers is possible only when the DMA channel is disabled.
16 // DMA ISR register cannot be written by the software in any case. It can be reset by the software by writing into to DMA_IFCR.
17 //--DONE-- 2. Implement functionality of DMA_IFCR
18 //--DONE-- 3. Optimization. Remove the 1+ from 1 + (2*chanNum) for id because we are anyways checking for read and write responses in different FIFOs
19 //--DONE-- 4. What to do if the DMA channel needs to be disabled in between? How to clear the FIFOs, and what to do about the pending on going transaction?
20 //5. Opt. Try to remove currentReadRs and currentWriteRs and use !destAddrFs.notEmpty instead to detect transfer complete and generate exception. Is generateTransferDoneRules even needed then?
21 //6. Parameterize the number of channels and peripherals completely.
22 //7. Write an assertion if currentReadRs[chan]==currentWriteRs[chan] then destAddrFs and responseDataFs are both empty. Prove this formally?
23 //8. Implement burst mode readConfig and writeConfig registers
24 //9. While joining generateTransferDoneRules, try using rJoinConflictFree instead of rJoinDescendingUrgency and check if they are formally equivalent. The conflict is between finishWrite and this rule. But the condition currentReadRs==currentWriteRs will not be true untill finishWrite fires. So, these two rules will never actually fire together.
33 import DefaultValue ::*;
34 import AXI4_Types :: *;
35 import AXI4_Fabric :: *;
36 import Semi_FIFOF :: *;
37 import ConcatReg :: *;
38 import ConfigReg :: *;
40 `define Burst_length_bits 8
41 `include "instance_defines.bsv"
43 // ================================================================
44 // DMA requests and responses parameters
47 function Bit#(1) fn_aligned_addr(Bit#(3) addr, Bit#(3) awsize);
51 if(addr == 0) out = 1;
54 if(addr[1:0] == 0) out = 1;
57 if(addr[0] == 0) out = 1;
70 typedef Bit#(`USERSPACE) Req_Info;
71 typedef Bit#(`PADDR) Req_Addr;
72 typedef Bit#(`Reg_width) Req_Data;
73 //The Req and Resp are of the same width in the current design
74 //typedef Bit#(10) RespInfo;
75 //typedef Bit#(1) RespAddr;
76 //typedef Bit#(32) RespData;
78 // ----------------------------------------------------------------
79 // At times it is best to consider registers as completely homogeneous,
80 // so that they can be accessed as a bit pattern with no internal
81 // structure. These functions convert reg interfaces based on a
82 // structured type to reg interfaces based on a bit pattern of at
83 // least the same width.
85 function Reg#(Bit#(n)) regAToRegBitN( Reg#(a_type) rin )
86 provisos ( Bits#( a_type, asize),
91 method Bit#(n) _read ();
92 a_type tmp = rin._read() ;
93 return zeroExtend (pack( tmp )) ;
95 method Action _write( Bit#(n) din );
96 rin._write( unpack( truncate(din) )) ;
101 // This function converts a Vector of 7 Registers to a single Register
102 function Reg#(Bit#(TMul#(7,q))) vector7ToRegN(Vector#(7,Reg#(Bit#(q))) inpV);
103 return concatReg7(inpV[6], inpV[5], inpV[4], inpV[3], inpV[2], inpV[1], inpV[0]);
104 //return asReg(zeroExtend(pack(inpV)));
107 // ================================================================
108 // The DMA interface has two sub-interfaces
109 // A AXI4 Slave interface for config
110 // A AXI4 Master interface for data transfers
112 interface DmaC #(numeric type numChannels, numeric type numPeripherals);
113 interface AXI4_Master_IFC#(`PADDR,`Reg_width,`USERSPACE) mmu;
114 interface AXI4_Slave_IFC#(`PADDR,`Reg_width,`USERSPACE) cfg;
115 method Action interrupt_from_peripherals(Bit#(numPeripherals) pint);
116 method Bit#(numChannels) interrupt_to_processor();
120 typedef UInt#(16) DMACounts ;
121 // The number of channels is a parameter.
122 // typedef 2 NumChannels;
125 // Several configuration registers are included, and connected to the
126 // config socket/fifo.
128 // Along with the destination address, if we are writing into a peripheral, we need to pass the peripheral id
129 // of the peripheral to check if the corresponding interrupt line is still high.
130 // Also, we need to send the destination transfer size for all the transactions.
134 Bit#(TLog#(numPeriphs)) periph_id;
135 } DestAddrFs_type#(numeric type numPeriphs) deriving (Bits,Eq);
138 Bit#(TLog#(numChannels)) chanNum;
140 } Disable_channel_type#(numeric type numChannels) deriving (Bits, Eq);
142 instance DefaultValue#(Disable_channel_type#(numChannels));
143 defaultValue= Disable_channel_type { chanNum: 'd-1,
147 (* descending_urgency = "writeConfig, handle_interrupts" *)
148 (* descending_urgency = "writeConfig, rl_finishRead" *)
149 (* descending_urgency = "writeConfig, rl_startWrite" *)
150 module mkDMA( DmaC #(numChannels, numPeripherals) )
151 provisos ( Add#(numChannels, 0, 7),
152 Add#(a__, TLog#(numPeripherals), 4));
154 // The DMA contains one master interface, and one slave interface. The processor sends
155 // request through the slave interface to set the config registers.
156 // The DMA's master initiates a request to one of the peripherals through one of the
157 // channels. The response is taken (through response sub-interface of DMA's Master interface
158 // and returned to the processor (through response sub-interface of the DMA's Slave interface
159 AXI4_Slave_Xactor_IFC #(`PADDR,`Reg_width,`USERSPACE) s_xactor <- mkAXI4_Slave_Xactor;
160 AXI4_Master_Xactor_IFC #(`PADDR,`Reg_width,`USERSPACE) m_xactor <- mkAXI4_Master_Xactor;
163 ////////////////////////////////////////////////////////////////
164 //////////////////////// DMA Registers /////////////////////////
165 ////////////////////////////////////////////////////////////////
166 Vector#(numChannels,Reg#(Bit#(4))) dma_isr <- replicateM(mkReg(0)); //Interrupt Status Register
167 Vector#(numChannels,Reg#(Bit#(4))) dma_ifcr <- replicateM(mkReg(0)); //Interrupt Flag Clear Register
168 Vector#(numChannels,Reg#(Bit#(32))) dma_ccr <- replicateM(mkConfigReg(0)); //Channel Configuration Register
169 Vector#(numChannels,Reg#(Bit#(16))) dma_cndtr <- replicateM(mkConfigReg(0)); //Channel Number of Data Transfer Register
170 Vector#(numChannels,Reg#(Req_Addr)) dma_cpar <- replicateM(mkReg(0)); //Channel Peripheral Address Register
171 Vector#(numChannels,Reg#(Req_Addr)) dma_cmar <- replicateM(mkReg(0)); //Channel Memory Address Register
172 Vector#(numChannels,Reg#(Bit#(4))) dma1_cselr <- replicateM(mkReg(0)); //Channel SELection Register
173 //We do not have dma2_cselr because there is only one DMA, and not 2 in this architecture
175 //Registers to keep track if all the data read is wrtten
176 //Vector#(numChannels, Array#(Reg#(DMACounts))) currentReadRs[2] <- replicateM(mkCReg(2,0));
177 //Vector#(numChannels, Array#(Reg#(DMACounts))) currentWriteRs[2] <- replicateM(mkCReg(2,0));
178 Reg#(DMACounts) currentReadRs[valueOf(numChannels)][2];
179 Reg#(DMACounts) currentWriteRs[valueOf(numChannels)][2];
180 Reg#(Bool) rg_is_cndtr_zero[valueOf(numChannels)][2];
181 Reg#(Bit#(8)) rg_write_strobe <- mkReg(0);
182 Reg#(Bit#(2)) rg_tsize <- mkReg(0);
183 for(Integer i=0 ; i<valueOf(numChannels) ; i=i+1) begin
184 currentReadRs[i] <- mkCReg(2,0);
185 currentWriteRs[i] <- mkCReg(2,0);
186 rg_is_cndtr_zero[i] <- mkCReg(2,True);
189 // Use a FIFO to pass the read response to the write "side",
190 // thus allowing pending transations and concurrency.
191 // FIFOs can be replicated as well.
192 Vector#(numChannels,FIFOF#(Req_Data))
193 responseDataFs <- replicateM(mkSizedFIFOF(2)) ;
195 //Wire to pass the interrupt from peripheral to DMA
196 Wire#(Vector#(numPeripherals,Bit#(1))) wr_peripheral_interrupt <- mkDWire(replicate(0));
198 //Wire to set the TEIF
199 Wire#(Maybe#(Bit#(4))) wr_bus_err <- mkDWire(tagged Invalid);
201 // We also want to pass the destination address for each read over
202 // to the write "side", along with some other metadata.
203 // The depth of this fifo limits the number of outstanding reads
204 // which may be pending before the write. The maximum outstanding
205 // reads depends on the overall latency of the read requests.
206 Vector#(numChannels,FIFOF#(DestAddrFs_type#(numPeripherals)))
207 destAddrFs <- replicateM( mkSizedFIFOF(2)) ;
209 // This register stores the initial value of the CNDTR. It is used to restore the value back
210 // when operating in circular mode.
211 Vector#(numChannels,Reg#(Bit#(16))) rg_cndtr <- replicateM(mkReg(0));
213 // The spec specifies that the CPAR and CMAR values when read in middle of a transaction should
214 // still hold the original programmed value, and not the address of the current transaction.
215 // Therefore, we have a copy of these registers which indicate the address of the current
216 // ongoing transaction on that channel.
217 Vector#(numChannels,Reg#(Req_Addr)) rg_cpa <- replicateM(mkConfigReg(0)); // Local Channel Peripheral Address Register
218 Vector#(numChannels,Reg#(Req_Addr)) rg_cma <- replicateM(mkConfigReg(0)); // Local Channel Memory Address Register
220 Reg#(Bit#(`Burst_length_bits)) rg_burst_count <- mkReg(0);
221 Reg#(Bit#(TLog#(numChannels))) rg_current_trans_chan_id <- mkReg(0);
222 Reg#(Tuple3#(Bool, Bit#(TLog#(numChannels)), Bit#(4))) rg_disable_channel <- mkReg(tuple3(False, ?, 'd-1));
223 Reg#(Tuple2#(Bit#(TLog#(numChannels)), Bit#(32))) rg_writeConfig_ccr <- mkReg(tuple2(0,0));
224 Reg#(Bool) rg_finish_write[valueOf(numChannels)][2];
225 Reg#(Bool) rg_finish_read[valueOf(numChannels)][2];
226 for(Integer i=0 ; i<valueOf(numChannels) ; i=i+1) begin
227 rg_finish_write[i]<- mkCReg(2,True);
228 rg_finish_read[i]<- mkCReg(2,True);
231 // This function returns the id of the peripheral for which this channel is configured
232 function Bit#(TLog#(numPeripherals)) fn_map_channel_to_periph(Integer chanNum);
233 return truncate(dma1_cselr[chanNum]);
236 // This function tells the maximum priority number amongst all the active peripherals
237 // which want to initiate a transaction. If one of the peripherals want to initiate a transaction
238 // i.e. it's interrupt line in high, we check if any other peripheral, whose interrupt line is high,
239 // has a higher priority (bits 13:12 in dma_ccrX). If so, we set the max_priority_num to
240 // the value of the highest interrupt.
241 // Note that even after this there might be multiple peripheral which have the same priority level as
242 // max_priority_num. Amongst them, the one with the lower channel number is given priority.
243 function Tuple2#(Bit#(TLog#(numChannels)),Bit#(TLog#(numPeripherals))) fn_dma_priority_encoder();
244 Bit#(2) max_priority_num= 0;
245 //stores id of the periph whose channel has been granted to generate req in this cycle.
246 Bit#(TLog#(numChannels)) grant_chan_id= 'd-1;
247 Bit#(TLog#(numPeripherals)) grant_periph_id= 'd-1;
248 for(Integer i=valueOf(numChannels)-1; i>=0; i=i-1) begin //for every channel
249 let periph_connected_to_channel_i=fn_map_channel_to_periph(i); //identify the peripheral connected to this channel
250 if(dma_ccr[i][0]==1 && dma_isr[i][1]==0 && //if the DMA channel is enabled
251 (wr_peripheral_interrupt[periph_connected_to_channel_i]==1 //if the peripheral has raised an interrupt
252 || dma_ccr[i][14]==1 //if M2M transfer, need not check for interrupt
253 //|| (dma_ccr[4]==1 && !rg_burst) //if not burst mode, the interrupt of periph needn't be high when reading from memory
254 //if in burst mode, its better if the peripheral is ready, and then we fetch the word from memory? TODO
256 //check if its priority is greater than any of the other peripherals' priority
257 //here we check for equality also as the channels with lower number have higher priority.
258 //therefore if two requests have same "high" software priority, the channel whose channel number
259 //is lower will be chosen
260 if(dma_ccr[i][13:12]>=max_priority_num) begin
261 max_priority_num= dma_ccr[i][13:12]; //if so, then set the current priority level to be that of peripheral[i]
262 grant_chan_id= fromInteger(i);
263 grant_periph_id= periph_connected_to_channel_i;
267 return tuple2(grant_chan_id,grant_periph_id);
270 function Bit#(16) fn_decr_cndtr(Bit#(16) cndtr, Bit#(2) tsize, Bit#(`Burst_length_bits) bsize);
271 Bit#(17) lv_to_sub= (zeroExtend(bsize)+1) << tsize;
272 Bit#(17) lv_result= {1'b0,cndtr}-lv_to_sub;
273 if(lv_result[16]==1) //underflow. Can happen in burst mode when the bytes to be transferred is not an exact multiple of the burst length x burst size.
276 return lv_result[15:0];
279 // This function increments the source or destination address depending on
280 // the size of the transfer.
281 // Note that though STM's DMA defines tsize=2'b11 as reserved, we use it to
282 // perform a 64-bit data transfer.
283 function Req_Addr fn_incr_address(Req_Addr addr, Bit#(2) tsize, Bit#(`Burst_length_bits) bsize) provisos(Bits#(Req_Addr,sz_Req_Addr), Add#(sz_Req_Addr,1, a));
284 Bit#(a) lv_to_add= (zeroExtend(bsize)+1) << tsize;
285 Bit#(a) lv_result= {1'b0,addr}+lv_to_add;
286 return truncate(lv_result);
289 //This function performs the data alignment for 64 bit data
290 function Bit#(64) fn_data_alignment64 (Bit#(64) data, Bit#(2) source_sz, Bit#(2) dest_sz);
292 case (source_sz) matches
294 outp= zeroExtend(data[7:0]);
298 outp= zeroExtend(data[7:0]);
300 outp= zeroExtend(data[15:0]);
304 outp= zeroExtend(data[7:0]);
305 else if(dest_sz==2'd1)
306 outp= zeroExtend(data[15:0]);
308 outp= zeroExtend(data[31:0]);
312 outp= zeroExtend(data[7:0]);
313 else if(dest_sz==2'd1)
314 outp= zeroExtend(data[15:0]);
315 else if(dest_sz== 2'd2)
316 outp= zeroExtend(data[31:0]);
324 // DMA rules //////////////////////////////////////////////////
325 // We define a function inside the module so it can access some
326 // of the registers without passing too many arguments.
327 // The function takes as arguments the conditions and fifos
329 // And returns a set a rules.
330 // The rule are identical to the set used in the one mmu port case.
331 function Rules generatePortDMARules (AXI4_Master_Xactor_IFC#(`PADDR,`Reg_width,`USERSPACE) xactor, Integer chanNum);
335 /*rule display_stat(dma_ccr[chanNum][0]==1 || tpl_1(rg_disable_channel));
336 Bit#(2) max_priority_num= 0;
337 //stores id of the periph whose channel has been granted to generate req in this cycle.
338 Bit#(TLog#(numChannels)) grant_chan_id= 'd-1;
339 Bit#(TLog#(numPeripherals)) grant_periph_id= 'd-1;
340 for(Integer i=valueOf(numChannels)-1; i>=0; i=i-1) begin //for every channel
341 let periph_connected_to_channel_i=fn_map_channel_to_periph(i); //identify the peripheral connected to this channel
342 //$display("\nChan %d interrupt: %b cndtr: 'h%h prio: %d", i, dma_isr[i][1], wr_peripheral_interrupt[periph_connected_to_channel_i], dma_cndtr[chanNum],tpl_1(fn_dma_priority_encoder()));
343 if(dma_ccr[i][0]==1 && dma_isr[i][1]==0 && //if the DMA channel is enabled
344 (wr_peripheral_interrupt[periph_connected_to_channel_i]==1 //if the peripheral has raised an interrupt
345 || dma_ccr[i][14]==1 //if M2M transfer, need not check for interrupt
346 //|| (dma_ccr[4]==1 && !rg_burst)
347 )) begin //if not burst mode, the interrupt of periph needn't be high when reading from memory
348 //check if its priority is greater than any of the other peripherals' priority
349 //here we check for equality also as the channels with lower number have higher priority.
350 //therefore if two requests have same "high" software priority, the channel whose channel number
351 //is lower will be chosen
352 if(dma_ccr[i][13:12]>=max_priority_num) begin
353 max_priority_num= dma_ccr[i][13:12]; //if so, then set the current priority level to be that of peripheral[i]
354 grant_chan_id= fromInteger(i);
355 grant_periph_id= periph_connected_to_channel_i;
356 //$display("########## grant chan: %d grant periph_id: %d", grant_chan_id, grant_periph_id);
358 //$display("dma_ccr[%d][13:12]: %b max_prio: %b",i,dma_ccr[i][13:12],max_priority_num);
361 if(pack(wr_peripheral_interrupt)!=0 || dma_ccr[chanNum][14]==1) begin
362 Reg#(Bit#(64)) lv_dma_ifcr= regAToRegBitN( vector7ToRegN( dma_ifcr ));
363 $display($time,"Chan%d: cndtr: %h dma_isr: %h dma_ifcr: %h", chanNum, dma_cndtr[chanNum], dma_isr[chanNum], lv_dma_ifcr );
365 //$display($time,"grant chan_id: %d periph_id: %d",grant_chan_id,grant_periph_id);
366 //$display("Channel no. %d user_id: %d", chanNum, xactor.o_rd_data.first.ruser);
369 // To start a read, following conditions need to be met
370 // 1. There is data to be transferred
371 // 2. The dma is enabled (dma_ccr[chanNum][0]=1)
372 // 3. The channel has the highest priority
373 // 4. If multiple channels have same high priority, choose the one whose channel number is lowest
374 // 5. A channel is disabled before the complete transfer is finished, do not initiate any more requests
375 // The 2nd, 3rd and 4th conditions are handled by fn_dma_priority_encoder
376 (* descending_urgency = "rl_startWrite, rl_startRead" *)
377 //(* preempts = "rl_finishRead, rl_can_change_channel" *)
378 //(* preempts = "rl_startWrite, rl_can_change_channel" *)
379 //(* preempts = "rl_send_burst_write_data, rl_can_change_channel" *)
380 rule rl_startRead ( !rg_is_cndtr_zero[chanNum][0] && //no of bytes remaining to transfer is not 0
381 fromInteger(chanNum) == tpl_1(fn_dma_priority_encoder()) && //if the this channel has the highest priority
382 (!tpl_1(rg_disable_channel) || tpl_2(rg_disable_channel)!=fromInteger(chanNum))
383 && rg_finish_read[chanNum][1]); //if the channel is not being disabled by the processor
387 let lv_dma_ccr= dma_ccr[chanNum];
388 Bit#(`Burst_length_bits) lv_burst= lv_dma_ccr[`Burst_length_bits+15:16]; //Upper bits of CCR supports configurable bursts !! Added, not part of STMicro
389 let lv_periph_id= tpl_2(fn_dma_priority_encoder());
391 if(lv_dma_ccr[6]==1) //peripheral increment mode is on
392 rg_cpa[chanNum]<= fn_incr_address(rg_cpa[chanNum], lv_dma_ccr[9:8], dma_ccr[chanNum][`Burst_length_bits+15:16]);
393 if(lv_dma_ccr[7]==1) //memory increment mode is on
394 rg_cma[chanNum]<= fn_incr_address(rg_cma[chanNum], lv_dma_ccr[11:10], dma_ccr[chanNum][`Burst_length_bits+15:16]);
396 if(lv_dma_ccr[4]==0) begin //read from peripheral
397 lv_araddr= rg_cpa[chanNum]; //set the address to read from
398 lv_arsize= lv_dma_ccr[9:8]; //set the transfer size
399 lv_burst_type= lv_dma_ccr[6]; //0: Fixed, 1: INCR which is consistent with that of AXI4
400 `ifdef verbose $display($time,"\tDMA: chan[%0d] starting read from peripheral address %h",chanNum, lv_araddr); `endif
401 // Since the destination is memory, the write request needn't wait for any interrupt line to be high
402 // Therefore, we send the first argument as Invalid
403 destAddrFs[chanNum].enq( DestAddrFs_type { addr: rg_cma[chanNum], // Enqueue the Write destination address
404 is_dest_periph: False,
405 periph_id: lv_periph_id});
407 else begin //read from memory
408 lv_araddr= rg_cma[chanNum]; //set the address to read from
409 lv_arsize= lv_dma_ccr[11:10]; //set the transfer size
410 lv_burst_type= lv_dma_ccr[7]; //0: Fixed, 1: INCR which is consistent with that of AXI4
411 `ifdef verbose $display($time,"\tDMA: chan[%0d] starting read from memory address %h",chanNum, lv_araddr); `endif
412 // Since the destination address is that of a peripheral, the write request can be issued only when
413 // the corresponding peripheral's interrupt line is high. Therefore, we send the periph_id too.
414 Bool lv_is_dest_periph;
415 if(lv_dma_ccr[14]==0)
416 lv_is_dest_periph= True;
418 lv_is_dest_periph= False;
419 `ifdef verbose $display("dest_is_periph: %h",lv_is_dest_periph); `endif
420 destAddrFs[chanNum].enq( DestAddrFs_type { addr: rg_cpa[chanNum], // Enqueue the Write destination address
421 is_dest_periph: lv_is_dest_periph,
422 periph_id: lv_periph_id});
426 // Create a read request, and enqueue it
427 // Since there can be multiple pending requests, either read or
428 // writes, we use the `Req_Info field to mark these.
429 let read_request = AXI4_Rd_Addr {araddr: lv_araddr,
430 arid: {1'b1,fromInteger(chanNum)}, arlen: lv_burst,
431 arsize: zeroExtend(lv_arsize), arburst: zeroExtend(lv_burst_type), //arburst: 00-FIXED 01-INCR 10-WRAP
434 xactor.i_rd_addr.enq(read_request);
435 `ifdef verbose $display("Sending a read request with araddr: %h arid: %h arlen: %h arsize: %h arburst: %h",lv_araddr,fromInteger(chanNum),lv_burst,lv_arsize,lv_burst_type); `endif
437 //housekeeping. To be done when the transaction is complete.
438 currentReadRs[chanNum][0]<= currentReadRs[chanNum][0] + 1;
439 dma_cndtr[chanNum]<= fn_decr_cndtr(dma_cndtr[chanNum], lv_arsize, dma_ccr[chanNum][`Burst_length_bits+15:16]);
440 rg_current_trans_chan_id<= fromInteger(chanNum);
441 rg_finish_read[chanNum][1]<= False;
444 // We finish the read when we see the correct respInfo on the mmu response fifo
445 rule rl_finishRead (xactor.o_rd_data.first.rid == {1'b1,fromInteger(chanNum)} && xactor.o_rd_data.first.rresp==AXI4_OKAY);
446 // update cndtr register to keep track of remaining transactions
447 let lv_dma_ccr= dma_ccr[chanNum];
449 Bit#(2) lv_source_size;
451 if(dma_ccr[chanNum][4]==0) begin //if the source is peripheral
452 lv_tsize= dma_ccr[chanNum][11:10]; //destination's tsize will be that of memory
453 lv_source_size= dma_ccr[chanNum][9:8];
456 lv_tsize= dma_ccr[chanNum][9:8]; //destination's tsize will be that of peripheral
457 lv_source_size= dma_ccr[chanNum][11:10];
460 // grab the data from the mmu reponse fifo
461 let resp <- pop_o(xactor.o_rd_data);
462 `ifdef verbose $display("DMA: chan[%d] finish read. Got data: %h",chanNum, resp.rdata); `endif
464 // Pass the read data to the write "side" of the dma
465 responseDataFs[chanNum].enq( resp.rdata );
466 rg_finish_read[chanNum][0]<= True;
469 //rule to handle circ mode
470 rule rl_handle_circ_mode(dma_ccr[chanNum][5]==1 && rg_is_cndtr_zero[chanNum][1]); //if circular mode is enabled
471 dma_cndtr[chanNum]<= rg_cndtr[chanNum];
474 // This rule start the write process
475 // Note that this rule conflicts with rule startRead, so we make
476 // this rule be more urgent. i.e., finish writing before you start
478 /*rule rl_startWrite111;
479 $display("helloo.. intrpt: %b burst_count: %h finish_write: %b", wr_peripheral_interrupt[ destAddrFs[chanNum].first.periph_id ], rg_burst_count, rg_finish_write[chanNum][1]);
482 rule rl_startWrite( (destAddrFs[chanNum].first.is_dest_periph==False //if the dest is memory
483 || wr_peripheral_interrupt[ destAddrFs[chanNum].first.periph_id ]==1) //if dest is not memory, then check if the peripheral's interrupt line is active
484 && rg_burst_count==0 && rg_finish_write[chanNum][1]==True);
485 let lv_data= destAddrFs[chanNum].first;
489 let lv_dma_ccr= dma_ccr[chanNum];
490 if(lv_dma_ccr[4]==0) begin //if the source is peripheral
491 lv_tsize= lv_dma_ccr[11:10]; //destination's tsize will be that of memory
492 lv_burst_type= lv_dma_ccr[7]; //destination's burst type will be that of memory
495 lv_tsize= lv_dma_ccr[9:8]; //destination's tsize will be that of peripheral
496 lv_burst_type= lv_dma_ccr[6]; //destination's burst type will be that of peripheral
499 Bit#(`Reg_width) actual_data= responseDataFs[chanNum].first;
500 Bit#(`Burst_length_bits) lv_burst_len= lv_dma_ccr[`Burst_length_bits+15:16];
501 // Bit#(6) x = {3'b0,lv_data.addr[2:0]}<<3;
502 Bit#(8) write_strobe=lv_tsize==0?8'b1:lv_tsize==1?8'b11:lv_tsize==2?8'hf:8'hff;
503 if(lv_tsize!=3)begin // 8-bit write;
504 //actual_data=actual_data<<(x);
505 write_strobe=write_strobe<<(lv_data.addr[`byte_offset:0]);
507 //lv_data.addr[2:0]=0; // also make the address 64-bit aligned
508 `ifdef verbose $display("Start Write"); `endif
512 if(lv_burst_len>0) begin // only enable the next rule when doing a write in burst mode.
513 rg_burst_count<=rg_burst_count+1;
515 `ifdef verbose $display("Starting burst mode write...."); `endif
519 $display("Performing a single write...");
523 rg_write_strobe <= write_strobe; //Write strobe needs to be rotated so that burst writes are sent correctly, storing write_strobe in a register. ~Vinod
524 rg_tsize <= lv_tsize; //Storing rg_tsize in a register. ~Vinod
526 let write_data = AXI4_Wr_Data { wdata: actual_data , wstrb: write_strobe, wlast: lv_last, wid: {1'b1,fromInteger(chanNum)}};
527 let write_addr = AXI4_Wr_Addr { awaddr: lv_data.addr, awuser: 0,
528 awlen: lv_burst_len, awsize: zeroExtend(lv_tsize), awburst: zeroExtend(lv_burst_type),
529 awid: {1'b1,fromInteger(chanNum)} };
531 // enqueue the request.
532 xactor.i_wr_data.enq(write_data);
533 xactor.i_wr_addr.enq(write_addr);
534 rg_finish_write[chanNum][1]<= False;
536 // Some other house keeping - removing the data from the fifos
537 responseDataFs[chanNum].deq;
538 destAddrFs[chanNum].deq; //dequeing this FIFO will cause startRead to fire.
539 `ifdef verbose $display ($time,"\tDMA[%0d] startWrite addr: %h data: %h", chanNum,lv_data.addr,responseDataFs[chanNum].first); `endif
542 //This rule is used to send burst write data. The explicit condition ensures that we
543 //send burst length number of data i.e. rg_burst_count>1. When rg_burst_count = the burst
544 //length specified in dma_ccr, rg_burst_count becomes 0. Since rg_burst_count!=0 infers
545 //lesser hardware compared to rg_burst_count>1, we write that as the explicit condition.
546 rule rl_send_burst_write_data(rg_burst_count!=0);// && dma_ccr[chanNum][`Burst_length_bits+15:16]!='d0);
547 Bool lv_last= rg_burst_count==dma_ccr[chanNum][`Burst_length_bits+15:16];
548 /*== Since this is going to always be a line write request in burst mode No need of shifting data and address=== */
549 let write_strobe = rotateBitsBy(rg_write_strobe,1<<rg_tsize); //~Vinod Rotating write_strobe by awsize
550 let w = AXI4_Wr_Data {wdata: responseDataFs[chanNum].first, wstrb: write_strobe , wlast: lv_last, wid: {1'b1, fromInteger(chanNum)} };
551 xactor.i_wr_data.enq(w);
552 `ifdef verbose $display ($time,"\tDMA[%0d] startWrite Burst data: %h rg_burst_count: %d dma_ccr[23:16]: %d", chanNum,responseDataFs[chanNum].first, rg_burst_count, dma_ccr[chanNum][23:16]); `endif
554 `ifdef verbose $display("Last data received..."); `endif
558 rg_burst_count<=rg_burst_count+1;
560 responseDataFs[chanNum].deq;
561 rg_write_strobe <= write_strobe;
565 // This rule waits for the write to finish
566 rule rl_finishWrite( (xactor.o_wr_resp.first.bid == {1'b1, fromInteger(chanNum)}) &&
567 (xactor.o_wr_resp.first.bresp==AXI4_OKAY) );
568 let x<- pop_o(xactor.o_wr_resp) ; // take the response data and finish
569 currentWriteRs[chanNum][0]<= currentWriteRs[chanNum][0] + 1;
570 `ifdef verbose $display ("DMA[%0d]: finishWrite", chanNum); `endif
571 rg_finish_write[chanNum][0]<= True;
574 rule rl_cndtr_is_zero;
575 rg_is_cndtr_zero[chanNum][0]<= (dma_cndtr[chanNum]==0);
581 function Rules generateTransferDoneRules( Integer chanNum );
584 // Conditions to mark when transfer is done.
585 // This rule will not fire for when circular mode because dma_cndtr[chanNum] is updated
586 // in the next cycle, which is before currentWriteRs can possibly get updated.
587 rule markTransferDone ( dma_ccr[chanNum][0]==1 && //if the channel is enabled
588 rg_is_cndtr_zero[chanNum][0] && //if the remaining data to transfer is 0
589 currentWriteRs[chanNum][0]== currentReadRs[chanNum][0]) ; //the final write has finished
590 //dmaEnabledRs[chanNum]._write (False) ;
591 currentWriteRs[chanNum][0] <= 0 ;
592 currentReadRs[chanNum][0] <= 0 ;
593 $display ("DMA[%0d]: transfer done", chanNum);
598 // Generate the rules, place them in priority order
600 Rules ruleset = emptyRules;
602 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
603 ruleset = rJoinDescendingUrgency (ruleset,
604 generatePortDMARules( m_xactor, ch));
607 for (Integer ch = 0; ch < valueof (numChannels); ch = ch + 1)
608 ruleset = rJoinDescendingUrgency (ruleset,
609 generateTransferDoneRules(ch));
613 (* descending_urgency = "rl_write_response_error, rl_read_response_error" *)
614 rule rl_write_response_error(m_xactor.o_wr_resp.first.bresp == AXI4_SLVERR || m_xactor.o_wr_resp.first.bresp == AXI4_DECERR);
615 wr_bus_err<= tagged Valid m_xactor.o_wr_resp.first.bid;
618 rule rl_read_response_error(m_xactor.o_rd_data.first.rresp == AXI4_SLVERR || m_xactor.o_rd_data.first.rresp == AXI4_DECERR);
619 wr_bus_err<= tagged Valid m_xactor.o_rd_data.first.rid;
622 rule handle_interrupts; //interrupts will be raised only when channel is enabled
623 for (Integer chanNum = 0; chanNum < valueOf (numChannels); chanNum = chanNum + 1) begin
624 Bit#(4) chan_isr= 'd0; //TEIF, HTIF, TCIF, GIF
625 Bit#(32) lv_dma_ccr= dma_ccr[chanNum];
626 //$display("*** chan: %d en: %d dma_cndtr: %h rg_cndtr: %h",chanNum, dma_ccr[chanNum][0], dma_cndtr[chanNum], rg_cndtr[chanNum]);
627 if(wr_bus_err matches tagged Valid .chan_num &&& fromInteger(chanNum)=={1'b1, chan_num[2:0]}) begin
629 $display("Bus error on channel %d",chanNum);
631 if(lv_dma_ccr[0]==1) begin
632 if(currentWriteRs[chanNum][1]==currentReadRs[chanNum][1]) begin //once the read and write transactions are over
633 if(rg_is_cndtr_zero[chanNum][0]) //TODO check what happens if you read from port 1 here
636 //The Half Transfer Complete will be set when half transfer is complete.
637 //Since dma_cndtr is modified by startRead rule, though it would've become half,
638 //the transaction wouldn't be complete till the write is over. Also, by the time write is over,
639 //another read request could've been issued. Therefore, we have the below condition.
640 if(dma_cndtr[chanNum]<=(rg_cndtr[chanNum]>>1) && rg_finish_write[chanNum][1]) begin
644 chan_isr[0]= chan_isr[3] | chan_isr[2] | chan_isr[1]; //Setting the GIF
645 chan_isr= dma_isr[chanNum][3:0] | chan_isr; //Sticky nature of interrupts. Should be cleared by software using ifcr.
647 //The bits in IFCR represent the interrupts that need to be cleared
648 chan_isr= chan_isr & ~(dma_ifcr[chanNum]);
649 dma_isr[chanNum]<= chan_isr;
650 // $display("chan_isr %b dma_cndtr[%d] :%h",chan_isr,chanNum,dma_cndtr[chanNum]);
655 // Rules and other code to interface config port /////////////
657 // Add a zero-size register as a default for invalid addresses
658 Reg#(Bit#(0)) nullReg <- mkReg( ? ) ;
660 // For ease of development we want all registers to look like 64
661 // bit resister-- the data size of the config socket.
662 // Create function to map from address to specific registers
663 function Tuple2#(Reg#(Req_Data), Bool) selectReg( Req_Addr addr );
664 Bit#(8) taddr = truncate( addr ) ;
667 8'h00 : return tuple2(regAToRegBitN( vector7ToRegN( dma_isr )), True);
668 8'h04 : return tuple2(regAToRegBitN( vector7ToRegN( dma_ifcr )), True);
669 8'hB0 : return tuple2(regAToRegBitN( vector7ToRegN( dma1_cselr )), True);
671 8'h08 : return tuple2(regAToRegBitN( dma_ccr[0] ), True); //32-bit
672 8'h0c : return tuple2(regAToRegBitN( dma_cndtr[0] ), True); //16-bit -- 32-bit Addr
673 8'h10 : return tuple2(regAToRegBitN( dma_cpar[0] ), True); //64-bit
674 8'h18 : return tuple2(regAToRegBitN( dma_cmar[0] ), True); //64-bit
676 8'h20 : return tuple2(regAToRegBitN( dma_ccr[1] ), True);
677 8'h24 : return tuple2(regAToRegBitN( dma_cndtr[1] ), True);
678 8'h28 : return tuple2(regAToRegBitN( dma_cpar[1] ), True);
679 8'h30 : return tuple2(regAToRegBitN( dma_cmar[1] ), True);
681 8'h38 : return tuple2(regAToRegBitN( dma_ccr[2] ), True);
682 8'h3C : return tuple2(regAToRegBitN( dma_cndtr[2] ), True);
683 8'h40 : return tuple2(regAToRegBitN( dma_cpar[2] ), True);
684 8'h48 : return tuple2(regAToRegBitN( dma_cmar[2] ), True);
686 8'h50 : return tuple2(regAToRegBitN( dma_ccr[3] ), True);
687 8'h54 : return tuple2(regAToRegBitN( dma_cndtr[3] ), True);
688 8'h58 : return tuple2(regAToRegBitN( dma_cpar[3] ), True);
689 8'h60 : return tuple2(regAToRegBitN( dma_cmar[3] ), True);
691 8'h68 : return tuple2(regAToRegBitN( dma_ccr[4] ), True);
692 8'h6C : return tuple2(regAToRegBitN( dma_cndtr[4] ), True);
693 8'h70 : return tuple2(regAToRegBitN( dma_cpar[4] ), True);
694 8'h78 : return tuple2(regAToRegBitN( dma_cmar[4] ), True);
696 8'h80 : return tuple2(regAToRegBitN( dma_ccr[5] ), True);
697 8'h84 : return tuple2(regAToRegBitN( dma_cndtr[5] ), True);
698 8'h88 : return tuple2(regAToRegBitN( dma_cpar[5] ), True);
699 8'h90 : return tuple2(regAToRegBitN( dma_cmar[5] ), True);
701 8'h98 : return tuple2(regAToRegBitN( dma_ccr[6] ), True);
702 8'h9C : return tuple2(regAToRegBitN( dma_cndtr[6] ), True);
703 8'hA0 : return tuple2(regAToRegBitN( dma_cpar[6] ), True);
704 8'hA8 : return tuple2(regAToRegBitN( dma_cmar[6] ), True);
706 default: return tuple2(regAToRegBitN( nullReg ), False);
710 function Bit#(3) ccr_channel_number (Req_Addr addr);
711 Bit#(8) taddr= truncate(addr);
725 Rules writeConfig = (rules
727 let write_addr <- pop_o(s_xactor.o_wr_addr);
728 let write_data <- pop_o(s_xactor.o_wr_data);
731 AXI4_Resp lv_bresp= AXI4_OKAY;
732 Bool lv_send_response= True;
734 if(write_data.wstrb=='hF0) begin
735 lv_data= zeroExtend(write_data.wdata[63:32]);
736 lv_addr= {truncateLSB(write_addr.awaddr),3'b100};
738 else if(write_data.wstrb=='h0F) begin
739 lv_data= zeroExtend(write_data.wdata[31:0]);
740 lv_addr= {truncateLSB(write_addr.awaddr),3'b000};
742 else if(write_data.wstrb=='hFF) begin
743 lv_data= write_data.wdata;
744 lv_addr= write_addr.awaddr;
746 else begin //The write request is not 64-bits, and therefore return a bus error
747 lv_bresp= AXI4_SLVERR;
748 $display($time,"\tDMA: KAT GAYA");
751 `ifdef verbose $display ($time,"\tDMA writeConfig addr: %0h data: %0h", lv_addr, lv_data); `endif
752 // Select and write the register
753 let lv1= selectReg(lv_addr);
754 let thisReg = tpl_1(lv1);
755 if(!tpl_2(lv1)) begin //if no register mapping exists for the given address
756 lv_bresp= AXI4_SLVERR;
757 $display($time,"\tDMA: Wapas KAT GAYA");
759 //else is not needed as the selectReg function handles it
761 let lv_ccr_channel_number= ccr_channel_number(lv_addr);
762 `ifdef verbose $display("ccr_channel_number %h lv_addr %h",lv_ccr_channel_number, lv_addr); `endif
763 if( lv_ccr_channel_number!='d-1 && tpl_2(lv1)) begin //if the current write is happening to one of the channel's CCR.
764 if(lv_data[0]==1 ) begin //if the channel is being enabled
765 rg_cpa[lv_ccr_channel_number] <= dma_cpar[lv_ccr_channel_number]; //peripheral address is copied
766 rg_cma[lv_ccr_channel_number] <= dma_cmar[lv_ccr_channel_number]; //memory address is copied
767 rg_cndtr[lv_ccr_channel_number]<= dma_cndtr[lv_ccr_channel_number]; //the cndtr value is saved
768 rg_disable_channel<= tuple3(False,?,?);
769 $display("----------------------- ENABLING DMA CHANNEL %d", lv_ccr_channel_number," -----------------------");
771 Bit#(3) cmar_align = dma_cmar[lv_ccr_channel_number][2:0]; //Vinod
772 Bit#(3) cpar_align = dma_cpar[lv_ccr_channel_number][2:0]; //Vinod
774 //lv_data[9:8] and lv_data[11:10] gives transfer size supposedly. Using K-Maps --Possibility of a bug?
775 bit cmar_is_aligned = fn_aligned_addr(write_addr.awaddr[2:0], write_addr.awsize);
776 bit cpar_is_aligned = fn_aligned_addr(write_addr.awaddr[2:0], write_addr.awsize);
778 if((cmar_is_aligned&cpar_is_aligned)==0) begin
779 lv_bresp = AXI4_DECERR; //DECERR for Unaligned addresses
780 $display("\tAXI4_DECERR\n");
783 $display("cmar_is_aligned: %b cpar_is_aligned: %b",cmar_is_aligned,cpar_is_aligned);
786 if(lv_data[4]==0) begin
787 $display("SOURCE: Peripheral Addr: 'h%0h Transfer size: 'b%b Incr: %b",dma_cpar[lv_ccr_channel_number], lv_data[9:8], lv_data[6]);
788 $display("DEST : Memory Addr: 'h%0h Transfer size: 'b%b Incr: %b",dma_cmar[lv_ccr_channel_number], lv_data[11:10], lv_data[7]);
790 else if(lv_data[14]==0) begin
791 $display("SOURCE: Memory Addr: 'h%0h Transfer size: 'b%b Incr: %b",dma_cmar[lv_ccr_channel_number], lv_data[11:10], lv_data[7]);
792 $display("DEST : Peripheral Addr: 'h%0h Transfer size: 'b%b Incr: %b",dma_cpar[lv_ccr_channel_number], lv_data[9:8], lv_data[6]);
795 $display("SOURCE: Memory Addr: 'h%0h Transfer size: 'b%b Incr: %b",dma_cmar[lv_ccr_channel_number], lv_data[11:10], lv_data[7]);
796 $display("DEST : Memory Addr: 'h%0h Transfer size: 'b%b Incr: %b",dma_cpar[lv_ccr_channel_number], lv_data[9:8], lv_data[6]);
798 $display("Priority level: 'b%b Circular mode: %b CNDTR: 'h%h", lv_data[13:12], lv_data[5], dma_cndtr[lv_ccr_channel_number]);
800 else begin //the channel is being disabled
801 //TODO since it is a CReg, what if we check in port [1]?
802 if(currentReadRs[lv_ccr_channel_number][0]!=currentWriteRs[lv_ccr_channel_number][0]) begin //there is an on going transaction
803 rg_disable_channel<= tuple3(True, lv_ccr_channel_number, write_addr.awid);
804 lv_send_response= False;
805 rg_writeConfig_ccr<= tuple2(lv_ccr_channel_number, truncate(lv_data));
807 else begin // no pending transaction
808 //clear the local registers
809 rg_is_cndtr_zero[lv_ccr_channel_number][0]<= True;
814 // Now generate the response and enqueue
815 if(lv_send_response) begin
817 let resp = AXI4_Wr_Resp { bresp: lv_bresp, buser: 0, bid: write_addr.awid };
818 s_xactor.i_wr_resp.enq(resp);
823 //Rule to send response to processor that the dma channel has been disabled after the on going transactions are over
824 Rules rl_send_chan_disabled_to_proc = (rules
825 rule rl_send_chan_disabled_to_proc(tpl_1(rg_disable_channel) && currentReadRs[tpl_2(rg_disable_channel)][1]==currentWriteRs[tpl_2(rg_disable_channel)][1]);
826 rg_disable_channel<= tuple2(False,?);
827 dma_ccr[tpl_1(rg_writeConfig_ccr)]<= tpl_2(rg_writeConfig_ccr);
828 let resp = AXI4_Wr_Resp { bresp: AXI4_OKAY, buser: 0, bid: tpl_3(rg_disable_channel) };
829 s_xactor.i_wr_resp.enq(resp);
833 //writeConfig gets highest priority since we do not want the core to stall
834 ruleset= rJoinDescendingUrgency(writeConfig,ruleset);
836 //writeConfig if more urgent than rl_send_chan_disabled_to_proc
837 ruleset= rJoinDescendingUrgency(ruleset, rl_send_chan_disabled_to_proc);
839 // A rule for reading a configuration register
840 //TODO need to add preempts with writeConfig? or mutually_exclusive? Because both these rules will never fire together.
841 // If we do not put any attributes, won't two instances of selectReg get synthesized?
844 let read_addr <- pop_o(s_xactor.o_rd_addr);
845 // Select the register
846 let lv1= selectReg(read_addr.araddr);
847 let thisReg = tpl_1(lv1);
849 //If read happens to a non defined register,
850 //or if read size is not 32-bits return SLVERR.
852 lv_rresp= AXI4_SLVERR;
857 if(read_addr.arsize=='b0)
858 lv_data={thisReg[7:0], thisReg[7:0], thisReg[7:0], thisReg[7:0], thisReg[7:0], thisReg[7:0], thisReg[7:0], thisReg[7:0]};
859 else if(read_addr.arsize=='b01)
860 lv_data={thisReg[15:0], thisReg[15:0], thisReg[15:0], thisReg[15:0]};
861 else if(read_addr.arsize=='b10)
862 lv_data={thisReg[31:0], thisReg[31:0]};
865 // Now generate the response and enqueue
866 let resp = AXI4_Rd_Data {rresp: lv_rresp, rdata: thisReg, rlast: True,
867 ruser: 0, rid: read_addr.arid};
868 s_xactor.i_rd_data.enq(resp);
872 // Add the rules to this module
875 //Note that unknownConfig rule is not needed here because all the FIFOs will
876 //be empty and hence neither writeConfig nor readConfig will fire.
878 ////////////////////////////////////////////////////////////////
879 ////////////////////////////////////////////////////////////////
881 // Create the interfaces by connecting the axi side interfaces
882 // of the transactors to it.
884 interface cfg= s_xactor.axi_side;
885 interface mmu= m_xactor.axi_side;
887 //This method receives various interrupts from the peripheral devices and gives it to the DMA
888 method Action interrupt_from_peripherals(Bit#(numPeripherals) pint);
889 wr_peripheral_interrupt<= unpack(pint);
892 //TODO should the interrupt be sent in the next cycle (as is implemented) or in the same cycle as generated (using Wires instead)?
893 //This method returns the interrupt generated by the DMA corresponding to every channel
894 //Raise the interrupt of a particular channel if any of the interrupts are active (in ISR) and are not masked(in CCR)
895 method Bit#(numChannels) interrupt_to_processor();
896 Bit#(numChannels) lv_interrupt_to_processor;
897 for(Integer chanNum= 0; chanNum < valueof(numChannels); chanNum= chanNum + 1) begin
898 let lv_dma_ccr= dma_ccr[chanNum];
899 //The bits in CCR represent the interrupts that are enabled, whereas the ones in IFCR represent which need to be cleared
900 let lv_intr_TE_HT_TC_enable= lv_dma_ccr[3:1];
902 //The bits in ISR represent which interrupts are active right now
903 Bit#(3) active_interrupts= {lv_intr_TE_HT_TC_enable} & dma_isr[chanNum][3:1];
904 lv_interrupt_to_processor[chanNum]= |(active_interrupts);
906 return lv_interrupt_to_processor;