DMA330ProgramWriter.Mod 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. MODULE DMA330ProgramWriter; (** AUTHOR "Timothée Martiel"; PURPOSE "Intermediate-level DMA interface for Zynq"; *)
  2. IMPORT SYSTEM, KernelLog, DMA330;
  3. CONST
  4. (* Status values *)
  5. (** No error *)
  6. Ok * = 0;
  7. (** Invalid burst size *)
  8. InvalidBurstSize * = 1;
  9. (** Invalid burst count *)
  10. InvalidBurstCount * = 2;
  11. (** Address must be aligned on burst size *)
  12. RequireAlignedAddress * = 3;
  13. (** Program must be generated first *)
  14. RequireProgramGeneration * = 4;
  15. (** Program must be bound to channel first *)
  16. RequireChannelBinding * = 5;
  17. (** Program must be initialized first *)
  18. RequireProgramInit * = 6;
  19. (** Specified channel is invalid *)
  20. InvalidChannel * = 7;
  21. (** Internal driver error *)
  22. InternalError * = 8;
  23. (** Both block sizes are 0, which is forbidden *)
  24. InvalidBlockSizes * = 9;
  25. (** MFIFO could not perform operation *)
  26. MfifoErr * = 12; (*! Do not change *)
  27. (** MFIFO lockup *)
  28. LockupErr * = 31; (*! Do not change *)
  29. (** Number of channels available *)
  30. MaxChannels * = 8;
  31. (** Burst parameters *)
  32. DefaultBurstCount = 16;
  33. DefaultBurstSize = 8;
  34. MaxBurstCount = 16;
  35. MaxBurstSize = 16;
  36. (* Program States *)
  37. (** Program was initialized *)
  38. Initialized = 1;
  39. Programmed = 2;
  40. (** Program was instanciated with src and dst addresses *)
  41. Instanciated = 3;
  42. (** Program was bound to a channel *)
  43. Bound = 4;
  44. (* Alignment properties *)
  45. None = 0;
  46. Dst = 1;
  47. Src = 2;
  48. (** Debugging and tracing output *)
  49. Trace = FALSE;
  50. TYPE
  51. (**
  52. DMA Program.
  53. *)
  54. Program * = RECORD
  55. programs: ARRAY MaxBurstSize OF RewritableProgram;
  56. src, dst: ADDRESS;
  57. active, align, burstSize -, burstCount -, channel -, dstBlockSize, dstStridingSize, srcBlockSize, srcStridingSize, size: LONGINT;
  58. state: SET;
  59. END;
  60. (**
  61. Handler type for DMA completion. 'status' indicates the if an error occurred. 'param' is a user-specified parameter.
  62. *)
  63. Handler * = PROCEDURE {DELEGATE} (status: LONGINT; param: ANY);
  64. HandlerDesc = RECORD
  65. handler: Handler;
  66. param: ANY
  67. END;
  68. (** Extension of the hardware-specific program to support addresses and event rewriting *)
  69. RewritableProgram = RECORD (DMA330.Program)
  70. dstOfs, sevOfs, srcOfs: LONGINT;
  71. created: BOOLEAN;
  72. END;
  73. VAR
  74. channelHandlers: ARRAY MaxChannels OF HandlerDesc;
  75. (** Initializes a DMA program with default burst sizes and counts. *)
  76. PROCEDURE InitProgram * (VAR program: Program);
  77. VAR
  78. ignore: LONGINT;
  79. BEGIN
  80. ASSERT(SetBurstParameters(program, DefaultBurstCount, DefaultBurstSize, ignore));
  81. program.state := {Initialized}
  82. END InitProgram;
  83. (**
  84. Overrides the burst parameters for a DMA program. Changes will be applied onyl after a call to Generate.
  85. DMA transfers data in bursts. Each burst consists of 'count' transfers of 'size' bytes. Depending on the underlying hardware, some
  86. counts and sizes may lead to more efficient transfers than others.
  87. *)
  88. PROCEDURE SetBurstParameters * (VAR program: Program; count, size: LONGINT; VAR status: LONGINT): BOOLEAN;
  89. BEGIN
  90. IF count > MaxBurstCount THEN
  91. status := InvalidBurstCount;
  92. RETURN FALSE
  93. END;
  94. IF size > MaxBurstSize THEN
  95. status := InvalidBurstSize;
  96. RETURN FALSE
  97. END;
  98. program.burstCount := count;
  99. program.burstSize := size;
  100. status := Ok;
  101. RETURN TRUE
  102. END SetBurstParameters;
  103. (**
  104. Generates a program for the specified transfer.
  105. A DMA transfer always transfers 'len' bytes from a source address 'src' to a destination address 'dst'. The ways data is read from the source or written to the destination depends on the parameters
  106. 'srcBlockSize', 'srcStridingSize' and 'dstBlockSize', 'dstStridingSize' respectively:
  107. - if '*BlockSize' is 0, the corresponding address is not incremented and all memory transfers are done from/to this address
  108. - if '*BlockSize' is not 0 and '*StridingSize' is 0, the corresponding address is incremented and the transfer occurs from/to a contiguous memory range of 'len' bytes starting at the address
  109. - if neither '*BlockSize' nor '*StridingSize' are 0, the transfer occurs from/to a discontinuous range of memory, consisting of contiguous blocks of '*BlockSize' bytes separated by '*StridingSize' bytes.
  110. So '*BlockSize' bytes are read/written, then '*StridingSize' bytes are skipped and this scheme is repeated until 'len' bytes have been transfered. 'len' must be a multiple of '*BlockSize'.
  111. *)
  112. PROCEDURE Generate * (
  113. VAR program: Program;
  114. src: ADDRESS; srcBlockSize, srcStridingSize: SIZE;
  115. dst: ADDRESS; dstBlockSize, dstStridingSize: SIZE;
  116. len: LONGINT;
  117. VAR status: LONGINT
  118. ): BOOLEAN;
  119. VAR
  120. iterations: LONGINT;
  121. result: BOOLEAN;
  122. incSrc, incDst: BOOLEAN;
  123. BEGIN
  124. ASSERT((program.align # None) OR (program.active = 0));
  125. IF program.align = Dst THEN
  126. ASSERT(dst MOD program.burstSize = program.active)
  127. ELSIF program.align = Src THEN
  128. ASSERT(src MOD program.burstSize = program.active)
  129. ELSIF program.align = None THEN
  130. IF dst MOD program.burstSize # 0 THEN
  131. program.active := dst MOD program.burstSize;
  132. program.align := Dst
  133. ELSIF src MOD program.burstSize # 0 THEN
  134. program.active := src MOD program.burstSize;
  135. program.align := Src
  136. END
  137. END;
  138. DMA330.InitProgram(program.programs[program.active]);
  139. program.programs[program.active].dstOfs := -1;
  140. program.programs[program.active].sevOfs := -1;
  141. program.programs[program.active].srcOfs := -1;
  142. result := TRUE;
  143. status := Ok;
  144. IF (srcBlockSize = 0) & (dstBlockSize = 0) THEN
  145. (* Transfer from fixed address to fixed address forbidden *)
  146. status := InvalidBlockSizes;
  147. result := FALSE
  148. ELSIF srcBlockSize = 0 THEN
  149. (* Transfer from fixed address *)
  150. IF dstStridingSize # 0 THEN
  151. (*! NOT IMPLEMENTED YET *)
  152. HALT(505)
  153. END;
  154. (*! ASSUMES ALIGNED ADDRESS *)
  155. ASSERT(dstBlockSize = len);
  156. incSrc := FALSE;
  157. incDst := TRUE
  158. ELSIF dstBlockSize = 0 THEN
  159. (* Transfer to fixed address *)
  160. IF srcStridingSize # 0 THEN
  161. (*! NOT IMPLEMENTED YET *)
  162. HALT(505)
  163. END;
  164. (*! ASSUMES ALIGNED ADDRESS *)
  165. ASSERT(srcBlockSize = len);
  166. incSrc := TRUE;
  167. incDst := FALSE
  168. ELSIF (srcStridingSize = 0) & (dstStridingSize = 0) THEN
  169. (* Transfer from cont to cont, aligned *)
  170. IF Trace THEN
  171. KernelLog.String("DMA Program Writer: ");
  172. KernelLog.String("generating aligned, cont -> aligned, cont program; size = ");
  173. KernelLog.Int(len, 0);
  174. KernelLog.String("; burst size = ");
  175. KernelLog.Int(program.burstSize, 0);
  176. KernelLog.String("; burst count = ");
  177. KernelLog.Int(program.burstCount, 0);
  178. KernelLog.Ln;
  179. END;
  180. (*! ASSUMES ALIGNED ADDRESSES *)
  181. ASSERT(srcBlockSize = len);
  182. ASSERT(dstBlockSize = len);
  183. incSrc := TRUE;
  184. incDst := TRUE
  185. ELSE
  186. (*! NOT IMPLEMENTED YET *)
  187. HALT(505);
  188. END;
  189. IF ~result THEN
  190. RETURN FALSE
  191. ELSIF (srcStridingSize = 0) & (dstStridingSize = 0) THEN
  192. WriteProgramHeader(program, src, incSrc, program.burstCount, program.burstSize, dst, incDst, program.burstCount, program.burstSize);
  193. IF (program.active > 0) & (program.align = Src) THEN
  194. WriteProgramSrcAlign(program, program.active, incSrc, incDst, ~incSrc)
  195. END;
  196. iterations := len DIV (program.burstCount * program.burstSize);
  197. ASSERT(iterations * program.burstCount * program.burstSize = len);
  198. LOOP
  199. IF iterations = 0 THEN EXIT END;
  200. IF iterations > 256 THEN
  201. iterations := WriteProgramNestedLoop(program, iterations, ~incSrc)
  202. ELSE
  203. iterations := WriteProgramLoop(program, iterations, ~incSrc)
  204. END
  205. END;
  206. (* DST alignment *)
  207. IF (program.active > 0) & (program.align = Dst) THEN
  208. WriteProgramDstAlign(program, program.active, incSrc, incDst, ~incSrc)
  209. END;
  210. program.programs[program.active].created := TRUE;
  211. INCL(program.state, Programmed)
  212. END;
  213. WriteProgramFooter(program);
  214. program.src := src;
  215. program.srcBlockSize := srcBlockSize;
  216. program.srcStridingSize := srcStridingSize;
  217. program.dst := dst;
  218. program.dstBlockSize := dstBlockSize;
  219. program.dstStridingSize := dstStridingSize;
  220. program.size := len;
  221. RETURN result
  222. END Generate;
  223. (** Changes the destination address of a program to 'adr'. *)
  224. PROCEDURE SetDst * (VAR program: Program; adr: ADDRESS; VAR status: LONGINT): BOOLEAN;
  225. VAR
  226. offset: LONGINT;
  227. result: BOOLEAN;
  228. BEGIN
  229. result := TRUE;
  230. status := Ok;
  231. program.active := adr MOD program.burstSize;
  232. ASSERT(program.align # Src);
  233. IF ~(Initialized IN program.state) THEN
  234. status := RequireProgramGeneration;
  235. result := FALSE
  236. ELSIF program.programs[program.active].created THEN
  237. program.align := Dst;
  238. offset := program.programs[program.active].offset;
  239. program.programs[program.active].offset := program.programs[program.active].dstOfs;
  240. DMA330.DMAMOV(program.programs[program.active], DMA330.DAR, adr);
  241. program.programs[program.active].offset := offset
  242. ELSE
  243. result := Generate(program, program.src, program.srcBlockSize, program.srcStridingSize, adr, program.dstBlockSize, program.dstStridingSize, program.size, status);
  244. END;
  245. RETURN result
  246. END SetDst;
  247. (** Changes the source address of a program to 'adr'. *)
  248. PROCEDURE SetSrc * (VAR program: Program; adr: ADDRESS; VAR status: LONGINT): BOOLEAN;
  249. VAR
  250. offset: LONGINT;
  251. result: BOOLEAN;
  252. BEGIN
  253. result := TRUE;
  254. status := Ok;
  255. program.active := adr MOD program.burstSize;
  256. IF ~(Initialized IN program.state) THEN
  257. status := RequireProgramGeneration;
  258. result := FALSE
  259. ELSIF program.programs[program.active].created THEN
  260. offset := program.programs[program.active].offset;
  261. program.programs[program.active].offset := program.programs[program.active].srcOfs;
  262. DMA330.DMAMOV(program.programs[program.active], DMA330.SAR, adr);
  263. program.programs[program.active].offset := offset
  264. ELSE
  265. result := Generate(program, program.src, program.srcBlockSize, program.srcStridingSize, adr, program.dstBlockSize, program.dstStridingSize, program.size, status);
  266. END;
  267. RETURN result
  268. END SetSrc;
  269. (** Binds the program 'prog' to the channel 'channel'. This must be done before program execution. *)
  270. PROCEDURE BindToChannel * (VAR program: Program; channel: LONGINT; VAR status: LONGINT): BOOLEAN;
  271. VAR
  272. offset: LONGINT;
  273. result: BOOLEAN;
  274. BEGIN
  275. result := TRUE;
  276. IF (channel < 0) OR (channel >= MaxChannels) THEN
  277. status := InvalidChannel;
  278. result := FALSE
  279. ELSIF program.programs[program.active].sevOfs = -1 THEN
  280. status := RequireProgramGeneration;
  281. result := FALSE
  282. ELSE
  283. program.channel := channel;
  284. offset := program.programs[program.active].offset;
  285. program.programs[program.active].offset := program.programs[program.active].sevOfs;
  286. IF DMA330.TracePrograms THEN KernelLog.String("Rewriting DMASEV:"); KernelLog.Ln END;
  287. DMA330.DMASEV(program.programs[program.active], channel);
  288. program.programs[program.active].offset := offset;
  289. END;
  290. RETURN result
  291. END BindToChannel;
  292. (** Starts executing the program. When the transfer is finished or triggers an error, 'handler' is called with the termination status and 'handlerParam' as parameters. *)
  293. PROCEDURE Execute * (CONST program: Program; handler: Handler; handlerParam: ANY; VAR status: LONGINT): BOOLEAN;
  294. VAR
  295. channel: LONGINT;
  296. result: BOOLEAN;
  297. BEGIN
  298. result := TRUE;
  299. IF ~(Programmed IN program.state) THEN
  300. status := RequireProgramGeneration;
  301. result := FALSE
  302. ELSIF program.channel = -1 THEN
  303. status := RequireChannelBinding;
  304. result := FALSE
  305. ELSE
  306. channel := program.channel;
  307. channelHandlers[channel].handler := handler;
  308. channelHandlers[channel].param := handlerParam;
  309. DMA330.StartDMAThread(program.programs[program.active], channel);
  310. END;
  311. RETURN result
  312. END Execute;
  313. (* Handler for normal transfer termination. *)
  314. PROCEDURE DoneHandler (channel: LONGINT);
  315. VAR
  316. handler: Handler;
  317. BEGIN
  318. handler := channelHandlers[channel].handler;
  319. IF handler # NIL THEN
  320. handler(Ok, channelHandlers[channel].param)
  321. END
  322. END DoneHandler;
  323. PROCEDURE FaultHandler (channel, fault: LONGINT);
  324. VAR
  325. handler: Handler;
  326. errors: SET;
  327. status: LONGINT;
  328. BEGIN
  329. (* Decode error *)
  330. errors := SYSTEM.VAL(SET, fault);
  331. IF channel = - 1 THEN
  332. status := InternalError
  333. ELSIF MfifoErr IN errors THEN
  334. status := MfifoErr
  335. ELSIF LockupErr IN errors THEN
  336. status := LockupErr
  337. ELSE
  338. status := InternalError
  339. END;
  340. (* Call handler *)
  341. handler := channelHandlers[channel].handler;
  342. IF handler # NIL THEN
  343. handler(status, channelHandlers[channel].param)
  344. END
  345. END FaultHandler;
  346. (** Writes the CCR, SAR and DAR registers of the channel to the values specified by the parameters. *)
  347. PROCEDURE WriteProgramHeader (VAR program: Program; src: ADDRESS; incSrc: BOOLEAN; srcBurstCount, srcBurstSize: LONGINT; dst: ADDRESS; incDst: BOOLEAN; dstBurstCount, dstBurstSize: LONGINT);
  348. BEGIN
  349. IF Trace THEN
  350. KernelLog.String("DMA Program Writer: ");
  351. KernelLog.String("program header; src = ");
  352. KernelLog.Address(src);
  353. KernelLog.String("; dst = ");
  354. KernelLog.Address(dst);
  355. KernelLog.Ln
  356. END;
  357. WriteProgramBurstParameters(program, srcBurstCount, srcBurstSize, dstBurstCount, dstBurstSize, incSrc, incDst);
  358. DMA330.DMAMOV(program.programs[program.active], DMA330.SAR, src);
  359. DMA330.DMAMOV(program.programs[program.active], DMA330.DAR, dst)
  360. END WriteProgramHeader;
  361. PROCEDURE WriteProgramBurstParameters (VAR program: Program; srcBurstCount, srcBurstSize, dstBurstCount, dstBurstSize: LONGINT; incSrc, incDst: BOOLEAN);
  362. VAR
  363. val: LONGINT;
  364. BEGIN
  365. val := DMA330.SB(srcBurstCount) + DMA330.SS(srcBurstSize * 8) + DMA330.DB(dstBurstCount) + DMA330.DS(dstBurstSize * 8);
  366. IF incSrc THEN INC(val, DMA330.SI) END;
  367. IF incDst THEN INC(val, DMA330.DI) END;
  368. DMA330.DMAMOV(program.programs[program.active], DMA330.CCR, val);
  369. END WriteProgramBurstParameters;
  370. PROCEDURE WriteProgramNestedLoop (VAR program: Program; iteration: LONGINT; read: BOOLEAN): LONGINT;
  371. VAR
  372. inner, outer: LONGINT;
  373. BEGIN
  374. outer := 256;
  375. LOOP
  376. IF outer = 0 THEN EXIT END;
  377. inner := iteration DIV outer;
  378. IF (inner * outer = iteration) & (inner <= 256) THEN EXIT END;
  379. DEC(outer);
  380. END;
  381. IF outer = 0 THEN
  382. inner := 256;
  383. outer := MIN(iteration DIV inner, 256)
  384. END;
  385. IF Trace THEN
  386. KernelLog.String("DMA Program Writer: ");
  387. KernelLog.String("iterations = "); KernelLog.Int(iteration, 0);
  388. KernelLog.String("; outer iterations = "); KernelLog.Int(outer, 0);
  389. KernelLog.String("; inner iterations = "); KernelLog.Int(inner, 0);
  390. KernelLog.Ln
  391. END;
  392. DMA330.DMALP(program.programs[program.active], outer - 1);
  393. DMA330.DMALP(program.programs[program.active], inner - 1);
  394. (*IF read THEN DMA330.DMAWFP(program.programs[program.active], 0, DMA330.Single) END;*)
  395. DMA330.DMALD(program.programs[program.active], FALSE, FALSE);
  396. DMA330.DMAST(program.programs[program.active], FALSE, FALSE);
  397. DMA330.DMALPEND(program.programs[program.active], FALSE, FALSE);
  398. DMA330.DMALPEND(program.programs[program.active], FALSE, FALSE);
  399. RETURN iteration - inner * outer
  400. END WriteProgramNestedLoop;
  401. PROCEDURE WriteProgramLoop (VAR program: Program; iteration: LONGINT; read: BOOLEAN): LONGINT;
  402. BEGIN
  403. IF Trace THEN
  404. KernelLog.String("DMA Program Writer: ");
  405. KernelLog.String("program loop with ");
  406. KernelLog.Int(iteration, 0);
  407. KernelLog.String(" iterations");
  408. KernelLog.Ln
  409. END;
  410. DMA330.DMALP(program.programs[program.active], iteration - 1);
  411. (*IF read THEN DMA330.DMAWFP(program.programs[program.active], 0, DMA330.Single) END;*)
  412. DMA330.DMALD(program.programs[program.active], FALSE, FALSE);
  413. DMA330.DMAST(program.programs[program.active], FALSE, FALSE);
  414. DMA330.DMALPEND(program.programs[program.active], FALSE, FALSE);
  415. RETURN 0
  416. END WriteProgramLoop;
  417. PROCEDURE WriteProgramSrcAlign (VAR program: Program; align: LONGINT; incSrc, incDst, read: BOOLEAN);
  418. VAR
  419. count, size: LONGINT;
  420. BEGIN
  421. (*IF read THEN DMA330.DMAWFP(program.programs[program.active], 0, DMA330.Single) END;*)
  422. DMA330.DMALD(program.programs[program.active], FALSE, FALSE);
  423. END WriteProgramSrcAlign;
  424. PROCEDURE WriteProgramDstAlign (VAR program: Program; align: LONGINT; incSrc, incDst, read: BOOLEAN);
  425. VAR
  426. count, size: LONGINT;
  427. BEGIN
  428. size := 4;
  429. LOOP
  430. count := 16;
  431. LOOP
  432. IF count * size = align THEN EXIT END;
  433. IF count = 1 THEN EXIT END;
  434. DEC(count)
  435. END;
  436. IF count * size = align THEN EXIT END;
  437. IF size = 1 THEN EXIT END;
  438. size := size DIV 2
  439. END;
  440. IF Trace THEN
  441. KernelLog.String("DMA Program Writer: ");
  442. KernelLog.String("compensating destination alignment; count = ");
  443. KernelLog.Int(count, 0);
  444. KernelLog.String("; size = ");
  445. KernelLog.Int(size, 0);
  446. KernelLog.Ln
  447. END;
  448. WriteProgramBurstParameters(program, program.burstCount, program.burstSize, count, size, incSrc, incDst);
  449. (*IF read THEN DMA330.DMAWFP(program.programs[program.active], 0, DMA330.Single) END;*)
  450. DMA330.DMALD(program.programs[program.active], FALSE, FALSE);
  451. DMA330.DMAST(program.programs[program.active], FALSE, FALSE);
  452. DEC(align, count * size)
  453. (*END*)
  454. END WriteProgramDstAlign;
  455. (** Writes a DMAWMB and a DMASEV(31) in the program *)
  456. PROCEDURE WriteProgramFooter (VAR program: Program);
  457. BEGIN
  458. DMA330.DMAWMB(program.programs[program.active]);
  459. program.programs[program.active].sevOfs := program.programs[program.active].offset;
  460. DMA330.DMASEV(program.programs[program.active], 31);
  461. DMA330.DMAEND(program.programs[program.active])
  462. END WriteProgramFooter;
  463. BEGIN
  464. DMA330.InstallDoneHandler(DoneHandler);
  465. DMA330.InstallFaultHandler(FaultHandler)
  466. END DMA330ProgramWriter.