UsbVarTdAlloc.Mod 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. MODULE UsbVarTdAlloc; (** AUTHOR "Timothée Martiel"; PURPOSE "Variable-size data-structure allocator for EHCI."; *)
  2. IMPORT Machine;
  3. CONST
  4. AllocSize = 1024 * 1024;
  5. AllocAlign = 1024 * 1024;
  6. TYPE
  7. (**
  8. Buffer descriptor
  9. *)
  10. TdBuffer = OBJECT
  11. VAR
  12. buffer: POINTER TO ARRAY OF CHAR;
  13. used: POINTER TO ARRAY OF SET;
  14. ofs: LONGINT;
  15. next: TdBuffer;
  16. PROCEDURE & Init (block: LONGINT);
  17. VAR
  18. bitmaskSize: LONGINT;
  19. BEGIN
  20. NEW(buffer, AllocSize + AllocAlign);
  21. ofs := AllocAlign - ADDRESSOF(buffer[0]) MOD AllocAlign;
  22. bitmaskSize := AllocSize DIV (block * SetSize);
  23. NEW(used, bitmaskSize)
  24. END Init;
  25. PROCEDURE Used(block: LONGINT): BOOLEAN;
  26. BEGIN
  27. RETURN (block MOD SetSize) IN used[block DIV SetSize];
  28. END Used;
  29. PROCEDURE SetUsed(block: LONGINT);
  30. BEGIN
  31. INCL(used[block DIV SetSize], block MOD SetSize);
  32. END SetUsed;
  33. (* faster version of SetUsed for blocks
  34. PROCEDURE SetUsedR(from, to: LONGINT);
  35. VAR startSet, stopSet, startBit, stopBit: LONGINT;
  36. BEGIN
  37. IF to < from THEN RETURN END;
  38. startBit := from MOD SetSize;
  39. stopBit := to MOD SetSize;
  40. startSet := from DIV SetSize;
  41. stopSet := to DIV SetSize;
  42. IF startSet < stopSet THEN
  43. used[startSet] := used[startSet] + {startBit .. MAX(SET)};
  44. INC(startSet);
  45. WHILE startSet < stopSet DO
  46. used[startSet] := {MIN(SET)..MAX(SET)};
  47. INC(startSet);
  48. END;
  49. used[stopSet] := used[stopSet] + {MIN(SET) .. stopBit};
  50. ELSE
  51. used[stopSet] := used[stopSet] + {startBit .. stopBit};
  52. END;
  53. END SetUsedR;
  54. *)
  55. PROCEDURE SetFree(block: LONGINT);
  56. BEGIN
  57. EXCL(used[block DIV SetSize], block MOD SetSize);
  58. END SetFree;
  59. (* faster version of SetFree for blocks
  60. PROCEDURE SetFreeR(from, to: LONGINT);
  61. VAR startSet, stopSet, startBit, stopBit: LONGINT;
  62. BEGIN
  63. IF to < from THEN RETURN END;
  64. startBit := from MOD SetSize;
  65. stopBit := to MOD SetSize;
  66. startSet := from DIV SetSize;
  67. stopSet := to DIV SetSize;
  68. IF startSet < stopSet THEN
  69. used[startSet] := used[startSet] - {startBit .. MAX(SET)};
  70. INC(startSet);
  71. WHILE startSet < stopSet DO
  72. used[startSet] := {};
  73. INC(startSet);
  74. END;
  75. used[stopSet] := used[stopSet] - {MIN(SET) .. stopBit};
  76. ELSE
  77. used[stopSet] := used[stopSet] - {startBit .. stopBit};
  78. END;
  79. END SetFreeR;
  80. *)
  81. END TdBuffer;
  82. (**
  83. Allocator.
  84. The allocator is created with a page size and a block size. It can then allocate memory blocks with a granularity of the block size.
  85. Each allocated block is guaranteed not to cross a page boundary.
  86. Allocated blocks must be freed manually.
  87. *)
  88. CONST SetSize = SIZEOF(SET) * 8;
  89. TYPE Allocator * = OBJECT
  90. VAR
  91. tdBuffers: TdBuffer;
  92. pageSize, blockSize, bitmaskSize: LONGINT;
  93. PROCEDURE & Setup * (pageSize, blockSize: LONGINT);
  94. BEGIN
  95. ASSERT(SetSize = 32);
  96. ASSERT((AllocSize MOD blockSize) MOD SetSize = 0);
  97. SELF.pageSize := pageSize;
  98. SELF.blockSize := blockSize;
  99. bitmaskSize := AllocSize DIV (blockSize * SetSize)
  100. END Setup;
  101. (** Allocate memory for a TD or a QH of the given size. The size must be a multiple of 32. *)
  102. PROCEDURE Allocate * (size: SIZE): ADDRESS;
  103. VAR
  104. buf: TdBuffer;
  105. start, pos, count: LONGINT;
  106. adr: ADDRESS;
  107. (** Allocate a new TD buffer and mark as used the last 32-byte block before a 4kB page boundary. *)
  108. PROCEDURE AllocateBuffer;
  109. VAR
  110. buf: TdBuffer;
  111. count, mod: LONGINT;
  112. BEGIN
  113. (* No buffer found: allocate a new one *)
  114. NEW(buf, blockSize);
  115. (*NEW(buf.buffer, AllocSize + AllocAlign);
  116. NEW(buf.used, bitmaskSize);*)
  117. buf.next := tdBuffers;
  118. tdBuffers := buf;
  119. count := 0;
  120. mod := ADDRESSOF(buf.buffer[0]) MOD AllocAlign;
  121. IF mod # 0 THEN
  122. buf.ofs := AllocAlign - mod
  123. END;
  124. Machine.DisableDCacheRange(ADDRESSOF(buf.buffer[buf.ofs]), AllocSize);
  125. (* Remove last 32-byte block before a 4kB page boundary from free blocks *)
  126. LOOP
  127. IF count >= AllocSize DIV blockSize THEN EXIT END;
  128. IF (ADDRESSOF(buf.buffer[buf.ofs + count * blockSize]) MOD pageSize) = pageSize - blockSize THEN
  129. buf.SetUsed(count);
  130. END;
  131. INC(count)
  132. END;
  133. END AllocateBuffer;
  134. BEGIN {EXCLUSIVE}
  135. ASSERT(size MOD blockSize = 0);
  136. size := size DIV blockSize;
  137. buf := tdBuffers;
  138. LOOP
  139. IF buf = NIL THEN
  140. AllocateBuffer;
  141. buf := tdBuffers;
  142. ASSERT(buf # NIL)
  143. END;
  144. count := 0;
  145. pos := 0;
  146. start := pos;
  147. WHILE (count < size) & (pos < bitmaskSize) DO
  148. IF buf.Used(pos) THEN
  149. count := 0;
  150. start := pos + 1;
  151. ELSE
  152. INC(count);
  153. END;
  154. INC(pos);
  155. END;
  156. IF count = size THEN EXIT END;
  157. buf := buf.next
  158. END;
  159. ASSERT(buf # NIL);
  160. adr := ADDRESSOF(buf.buffer[buf.ofs + start*blockSize]);
  161. (* faster version:
  162. buf.SetUsedR(start, start+count-1);
  163. *)
  164. WHILE(count > 0) DO
  165. ASSERT(~buf.Used(start));
  166. buf.SetUsed(start);
  167. INC(start); DEC(count);
  168. END;
  169. Machine.Fill32(adr, size * blockSize, 0);
  170. RETURN adr;
  171. END Allocate;
  172. (** Marks a TD as free, so that its memory can be used again *)
  173. PROCEDURE Free * (td: ADDRESS; size: SIZE);
  174. VAR
  175. buf: TdBuffer;
  176. adr: ADDRESS;
  177. slot: LONGINT;
  178. BEGIN {EXCLUSIVE}
  179. ASSERT(size MOD blockSize = 0);
  180. size := size DIV blockSize;
  181. buf := tdBuffers;
  182. LOOP
  183. IF buf = NIL THEN EXIT END;
  184. adr := ADDRESSOF(buf.buffer[buf.ofs]);
  185. IF (adr <= td) & (td < adr + AllocSize) THEN EXIT END;
  186. buf := buf.next
  187. END;
  188. ASSERT(buf # NIL); (* Not a TD *)
  189. slot := (td - adr) DIV blockSize;
  190. (* faster version:
  191. buf.SetFreeR(slot, slot+size-1);
  192. *)
  193. WHILE (size > 0) DO
  194. ASSERT(buf.Used(slot));
  195. buf.SetFree(slot);
  196. INC(slot); DEC(size);
  197. END;
  198. END Free;
  199. END Allocator;
  200. StaticAllocator * = OBJECT
  201. VAR
  202. buffers: TdBuffer;
  203. size: LONGINT;
  204. PROCEDURE & Setup * (allocSize: LONGINT);
  205. BEGIN
  206. size := allocSize
  207. END Setup;
  208. PROCEDURE Allocate * (): ADDRESS;
  209. BEGIN {EXCLUSIVE}
  210. END Allocate;
  211. PROCEDURE Free * (td: ADDRESS);
  212. BEGIN {EXCLUSIVE}
  213. END Free;
  214. END StaticAllocator;
  215. (*VAR
  216. padding: POINTER TO ARRAY OF CHAR;
  217. BEGIN
  218. NEW(padding, 1024 * 1024)*)
  219. END UsbVarTdAlloc.
  220. (* test module, uncomment for running a randomized test
  221. MODULE TestUsbVarTdAlloc; (** AUTHOR ""; PURPOSE ""; *)
  222. IMPORT UsbVarTdAlloc, Random;
  223. PROCEDURE Test*;
  224. VAR allocator: UsbVarTdAlloc.Allocator; adr: POINTER TO ARRAY OF ADDRESS;
  225. gen: Random.Generator; i,j, k: LONGINT; size: POINTER TO ARRAY OF SIZE;
  226. BEGIN
  227. NEW(allocator, 4096, 32);
  228. NEW(gen);
  229. NEW(adr, 1024); NEW(size, 1024);
  230. FOR j := 0 TO 100 DO
  231. FOR i := 0 TO LEN(adr)-1 DO
  232. size[i] := 32+gen.Dice(120)*32;
  233. adr[i] := allocator.Allocate(size[i]);
  234. ASSERT(adr[i] MOD 32 = 0);
  235. ASSERT(adr[i] DIV 4096 = (adr[i] + size[i]) DIV 4096);
  236. FOR k := 0 TO i-1 DO
  237. ASSERT(adr[k] # adr[i]);
  238. END;
  239. END;
  240. FOR i := 0 TO LEN(adr)-1 DO
  241. allocator.Free(adr[i], size[i]);
  242. END;
  243. TRACE(j);
  244. END;
  245. TRACE("done");
  246. END Test;
  247. END TestUsbVarTdAlloc.
  248. SystemTools.Free TestUsbVarTdAlloc UsbVarTdAlloc ~
  249. TestUsbVarTdAlloc.Test ~
  250. *)