1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2% Copyright (c) 2018, ETH Zurich.
3% All rights reserved.
4%
5% This file is distributed under the terms in the attached LICENSE file.
6% If you do not find this file, copies can be found by writing to:
7% ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich.
8% Attn: Systems Group.
9%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10
11% Some Conventions:
12% NodeId = identifier list
13% IAddr = [1,2,3]
14% Addr = [kind, [1,2,3]]
15% IBlock [block{..}, block{...}]
16% Block = [kind, [block{..}, block{..}]]
17
18:- module(decoding_net2).
19
20:- use_module(allocator).
21
22
23%% node_accept(InNodeId, InAddr :: block).
24:- dynamic node_accept/2.
25
26%% node_translate_dyn(InNodeId, InAddr :: block, OutNodeId, OutAddr :: block).
27:- dynamic node_translate_dyn/4.
28
29%% node_overlay(InNodeId, OutNodeId).
30:- dynamic node_overlay/2.
31
32%% This fact keeps track of blocks that are in use.
33%% node_in_use(NodeId, Block).
34:- dynamic node_in_use/2.
35
36%:- export node_accept/2.
37%:- export node_overlay/2.
38%:- export node_in_use/2.
39:- export struct(block(base,limit,props)).
40:- export struct(region(node_id,blocks)).
41:- export struct(name(node_id,address)).
42
43:- lib(ic).
44
45%% True, if any possible matching between two node ids A and B may exist.
46%node_reachable(A,A).
47%node_reachable(A,B) :-
48%    node_translate_dyn(A,_,Mid,_),
49%    node_reachable(Mid, B).
50%
51%node_reachable(A,B) :-
52%    node_block_meta(A,_,Mid),
53%    node_reachable(Mid, B).
54
55iaddress_aligned([], _).
56iaddress_aligned([A | As], Bits) :-
57    BlockSize is 2^Bits,
58    BlockNum #>= 0,
59    A #= BlockNum * BlockSize,
60    iaddress_aligned(As, Bits).
61
62address_aligned([_, IAddress], Bits) :-
63    iaddress_aligned(IAddress, Bits).
64
65name_aligned(Name, Bits) :-
66    name{address: Addr} = Name,
67    address_aligned(Addr, Bits).
68
69test_alignment :-
70    iaddress_gt([536870912], IAddr),
71    iaddress_aligned(IAddr, 21),
72    labeling(IAddr),
73    writeln(IAddr).
74
75test_alignment2 :-
76    init, add_pci, add_process,
77    Proc = region{node_id: ["OUT", "PROC0", "PROC0"]},
78    free_region_aligned(Proc, [memory, [1024]]),
79    writeln(Proc).
80
81iblock_match(A, block{base: B, limit: L}) :-
82    B #=< A,
83    A #=< L.
84
85iblock_nomatch(A, block{base: B, limit: L}) :-
86    A #< B ;
87    A #> L.
88
89iblocks_match_any(A, [B | Bs]) :-
90    iblock_match(A, B) ; iblocks_match_any(A, Bs).
91
92iblocks_match_any_ic(A, B) :-
93    iblocks_match_any(A,B),
94    labeling([A]).
95
96% Union of blocks. [block{base:0,limit:5},block{base:33,limit:35}] -> 0,1,..,5,33,..,35
97iblock_values(Blocks, Values) :-
98    findall(X, iblocks_match_any_ic(X, Blocks), Values).
99
100
101iblocks_match([], []).
102iblocks_match([A|As], [B|Bs]) :-
103    iblock_match(A,B),
104    iblocks_match(As, Bs).
105
106iblocks_nomatch([], []).
107iblocks_nomatch([A|As], [B|Bs]) :-
108    iblock_nomatch(A,B),
109    iblocks_nomatch(As, Bs).
110
111
112% For a ic constrained variable
113iblocks_match_ic(X,Bs) :-
114    length(Bs,LiLe),
115    length(X,LiLe),
116    iblocks_match(X, Bs),
117    labeling(X).
118
119% Cross product of blocks
120iblock_crossp(Blocks, Values) :-
121    findall(X, iblocks_match_ic(X, Blocks), Values).
122
123
124address_match([K, IAddr], [K, IBlocks]) :-
125    iblocks_match(IAddr, IBlocks).
126
127iaddress_gt([], []).
128iaddress_gt([S | Ss], [B | Bs]) :-
129    S #< B,
130    iaddress_gt(Ss,Bs).
131
132% Will only compare addresses of the same kind
133address_gt([K, ISmaller], [K, IBigger]) :-
134    iaddress_gt(ISmaller, IBigger).
135
136iaddress_gte([], []).
137iaddress_gte([S | Ss], [B | Bs]) :-
138    S #=< B,
139    iaddress_gte(Ss,Bs).
140
141% Will only compare addresses of the same kind
142address_gte([K, ISmaller], [K, IBigger]) :-
143    iaddress_gte(ISmaller, IBigger).
144
145iaddress_sub([], [], []).
146iaddress_sub([A | As], [B | Bs], [C | Cs]) :-
147    C is A - B,
148    iaddress_sub(As,Bs,Cs).
149
150% A - B = C ---> address_sub(A,B,C)
151address_sub([K, IA], [K, IB], [K, IC]) :-
152    iaddress_sub(IA, IB, IC).
153
154iaddress_add([], [], []).
155iaddress_add([A | As], [B | Bs], [C | Cs]) :-
156    C is A + B,
157    iaddress_add(As,Bs,Cs).
158
159% A + B = C ---> address_add(A,B,C)
160address_add([K, IA], [K, IB], [K, IC]) :-
161    iaddress_add(IA, IB, IC).
162
163iaddress_add_const_ic([], _, []).
164iaddress_add_const_ic([A | As], B, [C | Cs]) :-
165    C #= A + B,
166    iaddress_add_const_ic(As,B,Cs).
167
168% A + B = C ---> address_add(A,B,C)
169address_add_const_ic([K, IA], B, [K, IC]) :-
170    iaddress_add_const_ic(IA, B, IC).
171
172iaddress_add_const([], _, []).
173iaddress_add_const([A | As], B, [C | Cs]) :-
174    C is A + B,
175    iaddress_add_const(As,B,Cs).
176
177% A + B = C ---> address_add(A,B,C)
178address_add_const([K, IA], B, [K, IC]) :-
179    iaddress_add_const(IA, B, IC).
180
181iaddress_var([A | As]) :-
182    var(A) ; iaddress_var(As).
183
184address_var([K, IA]) :-
185    var(K) ; iaddress_var(IA).
186
187iblock_iaddress_gt([], []).
188iblock_iaddress_gt([Block | Bs], [Addr | As]) :-
189    block{
190        limit: Limit
191    } = Block,
192    Addr #>  Limit,
193    iblock_iaddress_gt(Bs, As).
194
195block_address_gt([K, IBlocks], [K, IAddress]) :-
196    iblock_iaddress_gt(IBlocks, IAddress).
197
198
199iblock_limit_iaddress([], []).
200iblock_limit_iaddress([Block | Bs], [Addr | As]) :-
201    block{
202        limit: Addr
203    } = Block,
204    iblock_limit_iaddress(Bs, As).
205
206% Turn the limit of the blocks into an address
207block_limit_address([K, IBlocks], [K, IAddress]) :-
208    iblock_limit_iaddress(IBlocks, IAddress).
209
210iblock_base_iaddress([], []).
211iblock_base_iaddress([Block | Bs], [Addr | As]) :-
212    block{
213        base: Addr
214    } = Block,
215    iblock_base_iaddress(Bs, As).
216
217% Turn the base of the blocks into an address
218block_base_address([K, IBlocks], [K, IAddress]) :-
219    (var(IBlocks),var(IAddress), fail) ;
220    iblock_base_iaddress(IBlocks, IAddress).
221
222% Turn a region into a base name
223region_base_name(Region, Name) :-
224    Region = region{node_id: NodeId, blocks: Blocks},
225    Name = name{node_id:NodeId, address: Base},
226    block_base_address(Blocks, Base).
227
228% Turn a region into a limit name
229region_limit_name(Region, Name) :-
230    Region = region{node_id: NodeId, blocks: Blocks},
231    block_limit_address(Blocks, Base),
232    Name = name{node_id:NodeId, address: Base}.
233
234iblock_isize([],[]).
235iblock_isize([A | As],[B | Bs]) :-
236    block{
237        base: Base,
238        limit: Limit
239    } = A,
240    (
241        (var(B), B is Limit - Base) ;
242        (var(Limit), Limit is Base + B)
243    ),
244    iblock_isize(As, Bs).
245
246block_size([K, IBlocks], [K, ISize]) :-
247    iblock_isize(IBlocks, ISize).
248
249region_size(Region, Size) :-
250    region{ blocks: Blocks } = Region,
251    block_size(Blocks, Size).
252
253:- export accept/2.
254accept(NodeId, Addr) :-
255    node_accept(NodeId, Block),
256    address_match(Addr, Block).
257
258iaddr_iblock_map([], [], [], []).
259iaddr_iblock_map([SrcAddr | A], [SrcBlock | B], [DstAddr | C], [DstBlock | D]) :-
260    SrcBlock = block{base:SrcBase},
261    DstBlock = block{base:DstBase},
262    DstAddr #= SrcAddr - SrcBase + DstBase,
263    iaddr_iblock_map(A,B,C,D).
264
265test_iaddr_iblock_map :-
266    iaddr_iblock_map([1],[block{base:0, limit:1024}], Dst, [block{base:100,limit:2000}]),
267    Dst = [101].
268
269%% Convert from region (encoded as block) to names
270to_name(Region,Name) :-
271    region{
272        node_id:Id,
273        blocks: Blocks % Blocks = [Kind, [block{...},block{...}]]
274    } = Region,
275    address_match(Addr, Blocks),
276    Name = name{
277        node_id:Id,
278        address:Addr
279    }.
280
281
282default_iaddr_constraint(Addr) :-
283    Addr #>= 0,
284    Addr #< 2147483648.
285
286%% Thes functions turn an IC constrained Addr to base/limit blocks
287iaddr_to_iblock_one(Addr, Block) :-
288    default_iaddr_constraint(Addr),
289    get_bounds(Addr,Min,Max),
290    Size is Max - Min + 1,
291    ( get_domain_size(Addr,Size) ->
292            Block = block{
293            base:Min,
294            limit:Max
295        }
296    ;
297        writeln(stderr,"Name conversion to region failed: Non continuous domain for address"),
298        fail
299    ).
300
301iaddr_to_iblocks([], []).
302iaddr_to_iblocks([A | As], [B | Bs]) :-
303    iaddr_to_iblock_one(A,B),
304    iaddr_to_iblocks(As, Bs).
305
306addr_to_blocks([K, IAddr], [K, IBlocks]) :-
307    iaddr_to_iblocks(IAddr, IBlocks).
308
309%% Convert from names to regions
310
311to_region(Name,Region) :-
312    name{
313        node_id:Id,
314        address:Addr
315    } = Name,
316    region{
317        node_id: Id,
318        blocks: Blocks
319    } = Region,
320    addr_to_blocks(Addr, Blocks).
321
322
323
324block_translate([SrcK, ISrcAddr], [SrcK, SrcBlock], [DstK, IDstAddr], [DstK, DstBlock]) :-
325    iaddr_iblock_map(ISrcAddr, SrcBlock, IDstAddr, DstBlock).
326
327test_block_translate :-
328    block_translate(
329        [memory,[1]],
330        [memory, [block{base:0, limit:1024}]],
331        Dst,
332        [memory, [block{base:100,limit:2000}]]),
333    Dst = [memory, [101]].
334
335
336does_not_translate(NodeId, [AKind,IAddr]) :-
337    %TODO take node_translate_block into account
338    findall(B, node_translate_dyn(NodeId, B, _, _), Blocks),
339    (foreach([AKind, IBlock], Blocks),param(IAddr),param(AKind) do
340        iblocks_nomatch(IAddr, IBlock)
341    ).
342
343test_does_not_translate :-
344    assert(node_translate_dyn(
345        ["In"], [memory, [block{base:1000,limit:2000}]],
346        ["Out1"], [memory, [block{base:0,limit:1000}]])),
347    does_not_translate(["In"], [memory, [500]]).
348
349
350% Takes translate and overlays predicates into account.
351%:- export translate/4.
352translate(SrcNodeId, SrcAddr, DstNodeId, DstAddr) :-
353    % The conditions check if, independently of Dst, a matching translate block
354    % exists. Only if it doesnt, we consider the overlay
355    (
356        node_translate_dyn(SrcNodeId, SrcBlock, CandidId, CandidBlock),
357        address_match(SrcAddr, SrcBlock),
358        DstNodeId = CandidId,
359        block_translate(SrcAddr, SrcBlock, DstAddr, CandidBlock)
360    ;
361        node_translate_block(SrcNodeId, SrcAddr, DstNodeId, DstAddr)
362    ;
363        does_not_translate(SrcNodeId, SrcAddr),
364        SrcAddr = DstAddr,
365        node_overlay(SrcNodeId, DstNodeId)
366    ).
367
368test_translate :-
369    add_pci,
370    translate(["IN", "IOMMU0", "PCI0"], [memory, [1]], OutNodeId, OutAddr),
371    OutNodeId = ["OUT", "IOMMU0", "PCI0"],
372    OutAddr = [memory, [1]].
373
374test_translate2 :-
375    %Setup
376    assert(node_translate_dyn(
377        ["In"], [memory, [block{base:1000,limit:2000}]],
378        ["Out1"], [memory, [block{base:0,limit:1000}]])),
379    assert(node_overlay(["In"], ["Out2"])),
380    % Test the translate block
381    translate(["In"], [memory, [1000]], ["Out1"], [memory, [0]]),
382    % Test the overlay
383    translate(["In"], [memory, [0]], ["Out2"], [memory, [0]]),
384    % make sure the upper limit is respected.
385    translate(["In"], [memory, [2500]], ["Out2"], [memory, [2500]]),
386    % make sure no within block translation to overlay exists
387    not(translate(["In"], [memory, [1000]], ["Out2"], [memory, [1000]])).
388
389
390accept_dummy(NodeId, AcS) :-
391    node_accept(NodeId, AcS).
392
393:- export accept/1.
394accept(Name) :-
395    name{
396        node_id:NodeId,
397        address:Addr
398    } = Name,
399    accept(NodeId, Addr).
400
401accept(Region) :-
402    region_base_name(Region,Base),
403    accept(Base),
404    region_limit_name(Region,Limit),
405    accept(Limit).
406
407
408:- export translate/2.
409translate(SrcName,DstName) :-
410    name{
411        node_id:SrcNodeId,
412        address:SrcAddr
413    } = SrcName,
414    name{
415        node_id:DstNodeId,
416        address:DstAddr
417    } = DstName,
418    translate(SrcNodeId,SrcAddr,DstNodeId, DstAddr).
419
420test_translate_name :-
421    add_pci,
422    translate(
423        name{node_id:["IN", "IOMMU0", "PCI0"], address: [memory, [1]]},
424        Out),
425    Out = name{node_id: ["OUT", "IOMMU0", "PCI0"], address: [memory, [1]]}.
426
427% Reflexive, transitive closure of translate
428:- export decodes_to/2.
429decodes_to(S,S).
430
431decodes_to(SrcName,DstName) :-
432    translate(SrcName,Name),
433    decodes_to(Name,DstName).
434
435:- export resolve/2.
436resolve(SrcName,DstName) :-
437    name{} = SrcName,
438    name{} = DstName,
439    decodes_to(SrcName,DstName),
440    accept(DstName).
441
442% TODO: Resolve on regions should work like route.
443resolve(SrcRegion,DstRegion) :-
444    to_name(SrcRegion,SrcName),
445    to_name(DstRegion,DstName),
446    resolve(SrcName,DstName),
447    to_region(SrcName,SrcRegion),
448    to_region(DstName,DstRegion).
449
450test_resolve :-
451    %Setup
452    assert(node_translate_dyn(
453        ["In"], [memory, [block{base:1000,limit:2000}]],
454        ["Out1"], [memory, [block{base:0,limit:1000}]])),
455    assert(node_overlay(["In"], ["Out2"])),
456    assert(node_accept(["Out1"], [memory,[block{base:0, limit:2000}]])),
457    assert(node_accept(["Out2"], [memory,[block{base:0, limit:2000}]])),
458    % Hit the translate block
459    resolve(
460        name{node_id:["In"], address:[memory, [1000]]},
461        name{node_id:["Out1"], address:[memory, [0]]}),
462    % Hit the overlay
463    resolve(
464        name{node_id:["In"], address:[memory, [500]]},
465        name{node_id:["Out2"], address:[memory, [500]]}).
466
467test_resolve2(Out) :-
468    %Setup
469    assert(node_translate_dyn(
470        ["In1"], [memory, [block{base:1000,limit:2000}]],
471        ["Out1"], [memory, [block{base:0,limit:1000}]])),
472    assert(node_translate_dyn(
473        ["In2"], [memory, [block{base:6000,limit:7000}]],
474        ["Out1"], [memory, [block{base:0,limit:1000}]])),
475    assert(node_accept(["Out1"], [memory,[block{base:0, limit:2000}]])),
476    % Reverse lookup
477    resolve(
478        name{node_id:["In1"], address:[memory, [1000]]},
479        R),
480    resolve(
481        name{node_id:["In2"], address:Out},
482        R).
483
484test_resolve3(Out) :-
485    %Setup
486    assert(node_translate_dyn(
487        ["In1"], [memory, [block{base:1000,limit:2000}]],
488        ["Out1"], [memory, [block{base:0,limit:1000}]])),
489    assert(node_translate_dyn(
490        ["In2"], [memory, [block{base:6000,limit:7000}]],
491        ["Out1"], [memory, [block{base:0,limit:1000}]])),
492    assert(node_accept(["Out1"], [memory,[block{base:0, limit:2000}]])),
493    InRegion = region{node_id:["In1"], blocks:[memory, [block{base:1000, limit:1500}]]},
494    resolve(InRegion,Out).
495
496
497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498%%%% Load sockeye compiled decoding nets and instantiate modules
499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500:- export load_net/1.
501load_net(File) :-
502    ensure_loaded(File).
503
504:- export load_module/2.
505load_module(Mod, Id) :-
506    call(Mod, Id).
507
508:- export load_net_module/2.
509load_net_module(File, Mod) :-
510    ensure_loaded(File),
511    call(Mod, []).
512
513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514%%%% Node enumeration.
515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516
517:- export alloc_node_enum/1.
518:- dynamic enum_node_id/2.
519:- export enum_node_id/2.
520alloc_node_enum(N) :- alloc_one(node_enum, N).
521
522get_or_alloc_node_enum(NodeId, Enum) :-
523    enum_node_id(Enum, NodeId) ;
524    ( alloc_node_enum(Enum), assert(enum_node_id(Enum, NodeId)) ).
525
526
527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
528%%%% VNode Allocator.
529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530
531:- dynamic vnode_region/1.
532
533vnode_meta(PageSize, PoolSize) :-
534    PageSize is 2 ^ 12, % 4Kb Pages
535    PoolSize is 2048.   % Number of pages
536
537% TODO: Test me
538vnode_alloc(BaseAddr) :-
539    vnode_region(Reg),
540    region_base_name(Reg, RegName),
541    alloc_one(vnodes, Slot),
542    vnode_meta(PageSize,_),
543    RegName = name{address: Addr},
544    Offset is PagesSize * Slot,
545    address_add_const(Addr, Offset, NewAddr),
546    NewAddr = [memory, [BaseAddr]].
547
548
549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
550%%%% X86 Support. Complements the sockeye file, should really be moved into its own file
551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
552
553:- export init/0.
554:- export add_pci_alloc/1.
555:- export add_pci/1.
556:- export add_process/1.
557:- export add_process_alloc/1.
558:- export dram_nodeid/1.
559:- export alloc_root_vnodeslot/2.
560:- export free_root_vnodeslot/2.
561
562:- dynamic pci_address_node_id/2.
563:- export pci_address_node_id/2.
564:- dynamic process_node_id/2.
565:- export process_node_id/2.
566
567alloc_root_vnodeslot(NodeId, Slot) :-
568    alloc_one(root_vnodeslot(NodeId), Tmp),
569    Slot is Tmp + 2.
570
571free_root_vnodeslot(NodeId, Slot) :-
572    Tmp is Slot - 2,
573    free_one(root_vnodeslot(NodeId), Tmp).
574
575dram_nodeid(NodeId) :- NodeId = ["DRAM"].
576
577% This uses the memory_region facts (defined in the main module) to
578% find a region above 4G that we will manage.
579initial_dram_block(Block) :- %a
580    % Find the usable DRAM using the existing SKB facts
581    call(mem_region_type, RamType, ram)@eclipse,
582    findall((Base, Size), call(memory_region,Base,Bits,Size,RamType,Data)@eclipse, MemCandidates),
583    (foreach((Base,Size), MemCandidates), fromto([], In, Out, FiltCandidates) do
584        (((MinBase = 4294967296, % 4G
585        MinSize = 1073741824, % 1G
586        Base >= MinBase,
587        Size >= MinSize) ->  Out = [(Base,Size) | In]
588        ) ; (
589        Out = In
590        ))
591    ),
592    FiltCandidates = [(Base,Size) | _],
593    Limit is Base + Size,
594    Block = block{base:Base, limit: Limit}.
595
596
597init :-
598    add_SYSTEM([]),
599    DRAM_ID = ["DRAM"],
600    initial_dram_block(Block),
601    assert(node_accept(["DRAM"], [memory, [Block]])),
602    get_or_alloc_node_enum(DRAM_ID, DRAM_ENUM),
603    printf("Decoding net initialized using %p as DRAM. DRAM nodeid: %p\n",
604        [Block, DRAM_ENUM]),
605
606    % Manage space for vnodes
607    vnode_meta(PageSize, PoolSize),
608    VnodePoolSize is PageSize * PoolSize,
609    Size = [VnodePoolSize],
610    alloc_range(DRAM_ID, [memory, Size], BaseOut),
611    mark_range_in_use(DRAM_ID, BaseOut, Size),
612    node_in_use(DRAM_ID, Region),
613    assert(vnode_region(Region)),
614    writeln("Using for PageTables:"), writeln(Region).
615
616add_pci :-
617    add_pci(["PCI0"]).
618
619iommu_enabled :-
620    call(iommu_enabled,0,_)@eclipse.
621
622add_pci(Id, Addr) :-
623    PCIBUS_ID = ["PCIBUS"],
624    PCIIN_ID = ["IN" | Id],
625    PCIOUT_ID = ["OUT" | Id],
626    (iommu_enabled -> (
627        add_PCI_IOMMU(Id),
628        % Mark IOMMU block remappable
629        assert(node_block_meta(["IN", "IOMMU0" | Id], 21, ["OUT", "IOMMU0" | Id])),
630        % And assign a root PT
631        pt_alloc(Root),
632        assert(node_pt(["IN", "IOMMU0", Id], Root, ["OUT","IOMMU0",Id]))
633    ) ; (
634        % IOMMU disabled.
635        add_PCI(Id)
636    )),
637    % connect the output to the systems pci bus
638    assert(node_overlay(PCIOUT_ID, PCIBUS_ID)),
639    % Now insert the BAR into the PCI bus address space
640    findall((Addr, BarNum, BarStart, BarSize),
641            call(bar(Addr, BarNum, BarStart, BarSize, mem, _, _))@eclipse,
642            Bars),
643    (foreach((_, BarNum, BarStart, BarSize), Bars), param(Id), param(PCIBUS_ID) do
644        BarId = [BarNum, "BAR" | Id],
645        BarEnd is BarStart + BarSize,
646        assert(node_accept(BarId, [memory,[block{base:BarStart,limit:BarEnd}]])),
647        assert(node_translate_dyn(PCIBUS_ID, [memory,[block{base:BarStart,limit:BarEnd}]], BarId,
648                              [memory, [block{base:BarStart,limit:BarEnd}]]))
649    ).
650
651
652add_pci_alloc(Addr) :-
653    alloc_node_enum(Enum),
654    add_pci([Enum], Addr),
655    % Set it to the node id where addresses are issued from the PCI device
656    OutNodeId = ["OUT", "PCI0", Enum],
657    assert(enum_node_id(Enum, OutNodeId)),
658    assert(pci_address_node_id(Addr, Enum)).
659
660add_process_alloc(Enum) :-
661    alloc_node_enum(Enum),
662    add_process([Enum]),
663    % Set it to the node id where addresses are issued from the process
664    assert(enum_node_id(Enum, ["OUT", "PROC0", Enum])).
665    %assert(process_node_id(ProcId, Enum)).
666
667
668% Make ID argument if we want to add multiple.
669add_process :-
670    add_process(["PROC0"]).
671
672add_process(Id) :-
673    DRAM_ID = ["DRAM"],
674    add_PROC_MMU(Id),
675
676    % Mark MMU block remappable
677    MMU_IN_ID = ["IN", "MMU0" | Id],
678    MMU_OUT_ID = ["OUT", "MMU0" | Id],
679    assert(node_block_meta(MMU_IN_ID, 21, MMU_OUT_ID)), % Make MMU configurable
680    pt_alloc(Root),
681    assert(node_pt(MMU_IN_ID, Root, MMU_OUT_ID)),
682
683    OUT_ID = ["OUT" | Id],
684    assert(node_overlay(OUT_ID, DRAM_ID)),
685    % Reserve memory for the process, the OUT/PROC0 node is the one where
686    % initially the process (virtual) addresses are issued.
687    Limit = 1099511627775, % (512 << 31) - 1
688    assert(node_in_use(["OUT", "PROC0" | Id], [memory, [block{base:0, limit: Limit}]])).
689
690
691
692%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
693%%%% Mark ranges used and Query them
694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
695
696:- export free_region/3.
697% Puts IC constraints on the variables
698free_region(NodeId, _, Out) :-
699   % Not a very smart allocator, finds the highest addr in use and append
700   % Therefore can ignore Size
701   findall(X, node_in_use(NodeId, X), UsedBlockLi),
702   block_address_gt([memory, [block{limit: -1}]], Out), % TODO: Works only for 1 Dim addr.
703   (foreach(UsedBlock, UsedBlockLi), param(Out) do
704       block_address_gt(UsedBlock, Out)
705   ).
706
707:- export free_region/2.
708% Puts IC constraints on the variables
709free_region(Name, Size) :-
710    name{
711        node_id: NodeId,
712        address: Out
713    } = Name,
714    free_region(NodeId, Size, Out).
715
716free_region(Region, Size) :-
717    region_base_name(Region, Name),
718    free_region(Name, Size).
719
720% Resolves the variables
721free_region_aligned(Region, Size) :-
722    is_list(Size),
723    region_base_name(Region, BaseName),
724    free_region(BaseName, Size),
725    name_aligned(BaseName, 21),
726    term_variables(BaseName, BaseNameVars),
727    labeling(BaseNameVars),
728    region_size(Region, Size).
729
730% Resolves the variables
731free_accepted_region_aligned(Region, Size) :-
732    is_list(Size),
733    region_base_name(Region, BaseName),
734    free_region(BaseName, Size),
735    name_aligned(BaseName, 21),
736    accept(BaseName),
737    term_variables(BaseName, BaseNameVars),
738    labeling(BaseNameVars),
739    region_size(Region, Size).
740
741%:- export free_region/1.
742%free_region(Region) :-
743%    region_size(Region, Size), % Determine size using the base/limit in the region.
744%    free_region(Region, Size).
745
746
747%% NodeId:: Addr, Size :: Addr, Out :: Addr
748:- export alloc_range/3.
749alloc_range(NodeId, Size, Out) :-
750   free_region(NodeId, Size, Out),
751   term_variables(Out, OutVars),
752   labeling(OutVars).
753
754
755% After finding a range with alloc range, you actually want to mark it used
756% with this function.
757mark_range_in_use(NodeId, Addr, ISize) :-
758    Addr = [Kind, IAddr],
759    (foreach(UsedBlock, UsedBlockLi), foreach(A, IAddr), foreach(S,ISize) do
760        Limit is A + S,
761        UsedBlock = block{
762            base: A,
763            limit: Limit
764        }
765    ),
766    assert(node_in_use(NodeId, [Kind, UsedBlockLi])).
767
768mark_range_in_use(Name, ISize) :-
769    name{
770        node_id: NodeId,
771        address: Addr
772    } = Name,
773    mark_range_in_use(NodeId, Addr, ISize).
774
775mark_range_in_use(Region) :-
776    Region = region{ node_id: NodeId, blocks: Blocks },
777    assert(node_in_use(NodeId, Blocks)).
778
779
780:- export mark_range_free/2.
781mark_range_free(NodeId, Base) :-
782    retract(node_in_use(NodeId, [memory, [block{base: Base}]])).
783
784:- export test_alloc_range/0.
785test_alloc_range :-
786    Id = [],
787    % Test setup
788    mark_range_in_use(Id, [memory, [0]], [1000]),
789
790    % First allocation
791    Size = [1000],
792    alloc_range(Id, [memory, Size], Out),
793    mark_range_in_use(Id, Out, Size),
794    writeln("First allocation: "),
795    writeln(Out),
796
797    % Second allocation
798    Size2 = [5000],
799    alloc_range(Id, [memory, Size2], Out2),
800    mark_range_in_use(Id, Out2, Size2),
801    writeln("Second allocation: "),
802    writeln(Out2).
803
804% Find a unused buffer, using already set up routing.
805% Node1 :: Addr, Node2 :: Name, Resolved :: Name
806common_free_buffer_existing(BufferSize, Node1, Node2, Resolved)  :-
807    free_region(Node1, [memory, [BufferSize]]),
808    free_region(Node2, [memory, [BufferSize]]),
809    resolve(Node1, Resolved),
810    resolve(Node2, Resolved),
811    free_region(Resolved, BufferSize),
812    term_variables(Resolved, Vars),
813    labeling(Vars).
814
815test_common_free_buffer_existing(Proc,Pci,Resolved) :-
816    init, add_pci, add_process,
817    BUFFER_SIZE = 1024,
818    Proc = name{node_id: ["OUT", "PROC0", "PROC0"]},
819    Pci = name{node_id: ["OUT", "PCI0", "PCI0"]},
820    common_free_buffer_existing(BUFFER_SIZE, Proc, Pci, Resolved).
821
822% Like common_free_buffer_existing, but allow reconfiguration of nodes (routing)
823% Find two regions N1Region and N2Region, that resolve to a free region.
824:- export common_free_buffer/5.
825common_free_buffer(Size, N1Region, N2Region, ResRegion, Route)  :-
826    is_list(Size),
827
828    N1Region = region{blocks: [memory, [_]]},
829    N2Region = region{blocks: [memory, [_]]},
830    ResRegion = region{blocks: [memory, [block{base:Base, limit: Limit}]]},
831
832    % nail down the regions
833    free_region_aligned(N1Region, Size),
834    free_region_aligned(N2Region, Size),
835    free_accepted_region_aligned(ResRegion, Size),
836    accept(ResRegion),
837
838    route(N1Region, ResRegion, R1),
839    route(N2Region, ResRegion, R2),
840
841    union(R1,R2,Route).
842
843% Find two regions N1Region and N2Region, that resolve to an existing result region.
844:- export common_free_map/5.
845common_free_map(Size, N1Region, N2Region, ResRegion,Route)  :-
846    is_list(Size),
847
848    N1Region = region{blocks: [memory, [_]]},
849    N2Region = region{blocks: [memory, [_]]},
850    ResRegion = region{blocks: [memory, [block{base:Base, limit: Limit}]]},
851
852    % nail down the input regions first
853    free_region_aligned(N1Region, Size),
854    free_region_aligned(N2Region, Size),
855
856    route(N1Region, ResRegion, R1),
857    route(N2Region, ResRegion, R2),
858
859    labeling([Base,Limit]),
860    union(R1,R2,Route).
861
862% the function called from mem_serv
863:- export alloc_common/4.
864% Allocate a Buffer with Bits size, reachable from N1 and N2. Mark the resolved
865% region as in use.
866alloc_common(Bits, N1Enum, N2Enum, DestEnum)  :-
867    enum_node_id(N1Enum, N1Id),
868    enum_node_id(N2Enum, N2Id),
869    enum_node_id(DestEnum, DestId),
870    R1 = region{node_id: N1Id, blocks: [memory, [block{base:R1Addr}]]},
871    R2 = region{node_id: N2Id, blocks: [memory, [block{base:R2Addr}]]},
872    Dest = region{node_id: DestId, blocks: [memory, [block{base:DestAddr}]]},
873    Size is 2 ^ Bits - 1,
874    common_free_buffer([memory, [Size]], R1, R2, Dest, _),
875    mark_range_in_use(Dest),
876    writeln([name(R1Addr, N1Enum),name(R2Addr, N2Enum),name(DestAddr, DestEnum)]).
877
878% Like alloc_common/4, but tries to determine destination node automatically.
879:- export alloc_common/3.
880alloc_common(Bits, N1Enum, N2Enum)  :-
881    DRAM = ["DRAM"],
882    get_or_alloc_node_enum(DRAM, DramEnum),
883    alloc_common(Bits, N1Enum, N2Enum, DramEnum).
884
885% Find names in N1 and N2 that resolve to ResAddr, then mark those used.
886:- export map_common/4.
887map_common(Bits, ResRAddr, N1Enum, N2Enum)  :-
888    enum_node_id(N1Enum, N1Id),
889    enum_node_id(N2Enum, N2Id),
890    R1 = region{node_id: N1Id, blocks: [memory, [block{base:R1Addr}]]},
891    R2 = region{node_id: N2Id, blocks: [memory, [block{base:R2Addr}]]},
892    Size is 2 ^ Bits - 1,
893    ResR = region{blocks: [memory, [block{base:ResRAddr}]]},
894    common_free_map([memory, [Size]], R1, R2, ResR, _),
895    ResR = region{node_id: ResRId},
896    get_or_alloc_node_enum(ResRId, ResEnum),
897    mark_range_in_use(R1),
898    mark_range_in_use(R2),
899    writeln([name(R1Addr, N1Enum),name(R2Addr, N2Enum),name(ResRAddr, ResEnum)]).
900
901
902% Translate a name from one Node to a name to another node, so they
903% resolve to the same ressource.
904:- export change_view/2.
905change_view(Name1, Name2) :-
906    resolve(Name1, D),
907    resolve(Name2, D).
908
909test_change_view(pci) :-
910    init, add_pci, add_process,
911    Proc = name{node_id: ["OUT", "PROC0", "PROC0"], address: [memory, [0]]},
912    Pci = name{node_id: ["OUT", "PCI0", "PCI0"]},
913    change_view(Proc, Pci).
914
915
916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
917%%%% Bit Array Representation
918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
919
920array_all_eq([], _).
921array_all_eq([A | Bs], A) :- array_all_eq(Bs,A).
922array_all_eq(Arr, A) :- array_list(Arr, ArrLi), array_all_eq(ArrLi, A).
923
924% Constrains word to be a N bit word
925assert_word(W, N) :-
926    dim(W,[N]), W :: [0 .. 1].
927
928% This beauty converts words (array of bit values) to a numeric representation.
929word_to_num(W, Num) :-
930    dim(W, [Len]),
931    (for(I,1,Len), fromto(0,In,Out,NumT), param(W) do
932        Out = W[I] * 2^(I-1) + In),
933    Num $= eval(NumT).
934
935% A part of word is etracted into Subword, Range specifies
936subword(Word,Subword, Range) :-
937    SW is Word[Range],
938    array_list(Subword,SW).
939
940
941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
942%%%% Block Remappable Nodes
943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
944
945% As of now, block remappable implies that the node is one dimension
946:- dynamic node_block_conf/3. %(NodeId, VPN, PPN).
947:- dynamic node_block_meta/3. %(NodeId, BlockSizeBits, OutNodeId)
948
949% Translate using Block Conf. Same signature as node_translate
950node_translate_block(InNodeId, [memory, [VAddr]], OutNodeId, [memory, [PAddr]]) :-
951    node_block_meta(InNodeId, BlockSizeBits, OutNodeId),
952    % Bit-Lookup Offset and VPN
953    assert_word(VAW, 48),
954    word_to_num(VAW, VAddr),
955    subword(VAW, VAOffsetW, 1 .. BlockSizeBits),
956    VPNStartBit is BlockSizeBits + 1,
957    subword(VAW, VPNW, VPNStartBit .. 48),
958    word_to_num(VPNW, VPN),
959
960    % Lookup PPN and PA offset
961    node_block_conf(InNodeId, VPN, PPN),
962
963    % Stich together PA
964    assert_word(PAW, 48), % TODO bit size for physical address?
965    subword(PAW, VAOffsetW, 1 .. 21),
966    subword(PAW, PPNW, 22 .. 48),
967    word_to_num(PPNW, PPN),
968
969    word_to_num(PAW, PAddr).
970
971test_node_translate_block :-
972    assert(node_block_conf([], 0, 1)),
973    assert(node_block_meta([], 21, ["OUT"])),
974    node_translate_block([], [memory,[1000]], ["OUT"], [memory, [2098152]]).
975
976
977%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
978%%%% Routing
979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
980
981one_block_upper_limit(Name, Limit) :-
982    Name = name{
983        node_id: NodeId,
984        address: Address
985    },
986    (( % Block remappable?
987      node_block_meta(NodeId, BlockSizeBits, _),
988      Address = [K, [A]],
989      Limit = [K, [ILimit]],
990      assert_word(AW, 48),
991      word_to_num(AW, A),
992      assert_word(LimitW, 48), % TODO bit size for physical address?
993      UpperStartBit is BlockSizeBits + 1,
994      subword(AW, UpperW, UpperStartBit .. 48),
995      subword(LimitW, UpperW, UpperStartBit .. 48),
996      subword(LimitW, LowerW, 1 .. BlockSizeBits),
997      array_all_eq(LowerW, 1),
998      word_to_num(LimitW, ILimit)
999    ) ; (
1000      % In a translate block?
1001      node_translate_dyn(NodeId, Block, _, _),
1002      address_match(Address, Block),
1003      block_limit_address(Block, Limit)
1004    ) ; (
1005      node_overlay(NodeId, _),
1006      Limit = [memory, [281474976710656]] % Default limit
1007    )).
1008
1009test_one_block_upper_limit :-
1010    assert(node_translate_dyn(
1011        ["In"], [memory, [block{base:1000,limit:2000}]],
1012        ["Out1"], [memory, [block{base:0,limit:1000}]])),
1013    one_block_upper_limit(name{node_id:["In"],address:[memory, [1400]]}, Limit),
1014    writeln(Limit),
1015
1016    assert(node_block_meta(["In1"], 21, ["Out1"])),
1017    one_block_upper_limit(name{node_id:["In1"],address:[memory, [1400]]}, Limit2),
1018    writeln(Limit2).
1019
1020% Internal function for region route
1021
1022% Route Step for names
1023route_step(SrcName, NextName, Route) :-
1024    SrcName = name{},
1025    NextName = name{},
1026    ((
1027        translate(SrcName, NextName),
1028        Route = []
1029    ) ; (
1030        % In this case, we can assume, there is no block map existing,
1031        % But, we have to check if the node supports block mapping, then
1032        % we can install this
1033        can_translate(SrcName, NextName, Config),
1034        Route = [Config]
1035    )).
1036
1037% Route Step for regions
1038route_step(SrcRegion, NextRegion, Route) :-
1039    % This will only work, if the source region will translate to the same node
1040    % block this should be ensured by the block splitting of the region route
1041    region_base_name(SrcRegion, SrcBase),
1042    region_limit_name(SrcRegion, SrcLimit),
1043    route_step(SrcBase, NextBase, Route1),
1044    route_step(SrcLimit, NextLimit, Route2),
1045    region_base_name(NextRegion, NextBase),
1046    region_limit_name(NextRegion, NextLimit),
1047    union(Route1, Route2, Route).
1048
1049% Routing functionality for ranges addresses (represented as region)
1050% This one
1051route(SrcRegion, DstRegion, Route) :-
1052    SrcRegion = region{node_id: SrcNodeId, blocks: SrcBlocks},
1053    DstRegion = region{node_id: DstNodeId, blocks: DstBlocks},
1054    block_base_address(SrcBlocks, SrcBase), % SrcBase = [memory, [0,1,2]]
1055    block_limit_address(SrcBlocks, SrcLimit), % SrcLimit = [memory, [10,11,12]]
1056    block_base_address(DstBlocks, DstBase),
1057    block_limit_address(DstBlocks, DstLimit),
1058    one_block_upper_limit(name{node_id:SrcNodeId, address:SrcBase}, BlockLimit),
1059    % BlockLimit = [memory, [2000]]
1060    ((
1061        address_gte(SrcLimit , BlockLimit),
1062        % Great, SrcRegion fits completly in translate block
1063        route_step(SrcRegion, NextRegion, R1),
1064        ( accept(NextRegion) -> (
1065            DstRegion = NextRegion,
1066            Route = R1
1067        ) ; (
1068            route(NextRegion, DstRegion, R2),
1069            union(R1, R2, Route)
1070        ))
1071    ) ; (
1072        % Only allow this if the block has to be split
1073        not(address_gte(SrcLimit, BlockLimit)),
1074
1075        % Route first block
1076        block_base_address(NewSrcBlocks, SrcBase), % Keep the base
1077        block_limit_address(NewSrcBlocks, BlockLimit),
1078
1079        block_base_address(NewDstBlocks, DstBase), % Keep the base
1080        address_sub(BlockLimit, SrcBase, BlockSize),
1081        address_add(DstBase, BlockSize, NewDstLimit),
1082        block_limit_address(NewDstBlocks, NewDstLimit),
1083        route(
1084            region{node_id:SrcNodeId, blocks: NewSrcBlocks},
1085            region{node_id:DstNodeId, blocks: NewDstBlocks},
1086            R1),
1087
1088        % Construct remainder
1089        address_add_const(BlockLimit, 1, AfterBlock),
1090        block_base_address(RemSrcBlocks, AfterBlock),
1091        block_limit_address(RemSrcBlocks, SrcLimit),  % keep limit
1092
1093        address_add_const(NewDstLimit, 1, AfterDstLimit),
1094        block_base_address(RemDstBlocks, AfterDstLimit),
1095        block_limit_address(RemDstBlocks, DstLimit), % keep limit
1096        route(
1097            region{node_id:SrcNodeId, blocks: RemSrcBlocks},
1098            region{node_id:DstNodeId, blocks: RemDstBlocks},
1099            R2),
1100
1101        % Concat routes
1102        union(R1, R2, Route)
1103    )).
1104
1105
1106% Routing functionality for single addresses (represented as name)
1107route(SrcName, DstName, Route) :-
1108    SrcName = name{},
1109    DstName = name{},
1110    route_step(SrcName, NextName, R1),
1111    ( accept(NextName) -> (
1112        DstName = NextName,
1113        Route = R1
1114    ) ; (
1115        route(NextName, DstName, R2),
1116        union(R1, R2, Route)
1117    )).
1118
1119route_and_install(SrcName, DstName, Route) :-
1120    route(SrcName,DstName, Route),
1121    term_variables(Route, RouteVars),
1122    labeling(RouteVars),
1123    install_route(Route).
1124
1125
1126test_route(Route) :-
1127    init, add_pci, add_process,
1128    MMU_IN_ID = ["IN", "MMU0", "PROC0"],
1129    MMU_OUT_ID = ["OUT", "MMU0", "PROC0"],
1130    DRAM_ID = ["DRAM"],
1131    retract(node_translate_dyn(MMU_IN_ID,_,_,_)), % Make sure there is no route
1132    assert(node_block_meta(MMU_IN_ID, 21, MMU_OUT_ID)),
1133    alloc_range(DRAM_ID, [memory, [1024]], FreeDRAM),
1134    SrcName=name{node_id: MMU_IN_ID, address: [memory, [0]]},
1135    DstName=name{node_id: DRAM_ID, address: FreeDRAM},
1136    writeln(("Routing from", SrcName, " to ", DstName)),
1137    route(SrcName, DstName, Route),
1138    term_variables(Route, RouteVars),
1139    labeling(RouteVars),
1140    writeln(("Calculated", Route)).
1141
1142test_route2 :-
1143    init, add_pci, add_process,
1144    MMU_IN_ID = ["IN", "MMU0", "PROC0"],
1145    MMU_OUT_ID = ["OUT", "MMU0", "PROC0"],
1146    DRAM_ID = ["DRAM"],
1147    retract(node_translate_dyn(MMU_IN_ID,_,_,_)), % Make sure there is no route
1148    assert(node_block_meta(MMU_IN_ID, 21, MMU_OUT_ID)),
1149    alloc_range(DRAM_ID, [memory, [1024]], FreeDRAM),
1150    SrcName1=name{node_id: MMU_IN_ID, address: [memory, [0]]},
1151    DstName1=name{node_id: DRAM_ID, address: FreeDRAM},
1152    writeln(("Routing from", SrcName1, " to ", DstName1)),
1153    route_and_install(SrcName1, DstName1, Route1),
1154    writeln(("Calculated (1)", Route1)),
1155    SrcName2=name{node_id: MMU_IN_ID, address: [memory, [0]]},
1156    DstName2=name{node_id: DRAM_ID, address: FreeDRAM},
1157    writeln(("Routing from", SrcName2, " to ", DstName2)),
1158    route_and_install(SrcName2, DstName2, Route2),
1159    writeln(("Calculated (2) ", Route2)).
1160
1161
1162test_route_region_small :-
1163    assert(node_block_meta(["IN"], 21, ["OUT"])),
1164    assert(node_accept(["OUT"], [memory, [block{base:0, limit:100000}]])),
1165    route(
1166        region{node_id:["IN"], blocks:[memory,[block{base:0, limit:1000}]]},
1167        region{node_id:["OUT"], blocks:[memory,[block{base: 0, limit: 1000}]]},
1168        Route),
1169    term_variables(Route, RouteVars),
1170    labeling(RouteVars),
1171
1172    % check if it worked
1173    member((["IN"], 0, 0), Route),
1174    writeln(("Calculated", Route)).
1175
1176test_route_region_two :-
1177    Limit = 4194302, % 0x1fffff * 2
1178    assert(node_block_meta(["IN"], 21, ["OUT"])),
1179    assert(node_accept(["OUT"], [memory, [block{base:0, limit:4194303}]])),
1180    route(
1181        region{node_id:["IN"], blocks:[memory,[block{base:0, limit:Limit}]]},
1182        region{node_id:["OUT"], blocks:[memory,[block{base: 0, limit: Limit}]]},
1183        Route),
1184    term_variables(Route, RouteVars),
1185    labeling(RouteVars),
1186
1187    member((["IN"], 0, 0), Route),
1188    member((["IN"], 1, 1), Route),
1189    writeln(("Calculated", Route)).
1190
1191
1192test_route_all :-
1193    test_route_region_small,
1194    writeln("region small: passed"),
1195    test_route_region_two,
1196    writeln("region two: passed").
1197
1198
1199% It is possible, to remap SrcName to DstName using the block remapping?
1200can_translate(SrcName, DstName, (SrcNodeId, (VPN, PPN))) :-
1201    name{ node_id: SrcNodeId, address: [K, [ISrcAddr]] } = SrcName,
1202    name{ node_id: DstNodeId, address: [K, [IDstAddr]] } = DstName,
1203    node_block_meta(SrcNodeId, BlockSizeBits, DstNodeId),
1204    split_vaddr(ISrcAddr, BlockSizeBits, [VPN, Offset]),
1205    split_vaddr(IDstAddr, BlockSizeBits, [PPN, Offset]),
1206    not(node_block_conf(SrcNodeId, VPN, _)). % No mapping with this VPN
1207
1208:- export install_route/1.
1209install_route([]).
1210
1211install_route([null | Ri]) :-
1212    install_route(Ri).
1213
1214install_route([(NodeId, VPN, PPN) | Ri]) :-
1215    assert(node_block_conf(NodeId, VPN, PPN)),
1216    install_route(Ri).
1217
1218
1219
1220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221%%%% X86 Page table configurable nodes
1222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1223
1224% node_pt stores the root page table for per node
1225:- dynamic node_pt/3. % NodeId, RootPt, OutNodeId
1226:- dynamic pt/3. % PtIndex, Offset, NextPtIndex
1227:- export pt/3.
1228
1229split_vpn(VPN, Parts) :-
1230    assert_word(VPNW, 27),  % 3 x 9 = 27
1231    word_to_num(VPNW, VPN),
1232    subword(VPNW, L1W, 1 .. 9),
1233    word_to_num(L1W, L1),
1234    subword(VPNW, L2W, 10 .. 18),
1235    word_to_num(L2W, L2),
1236    subword(VPNW, L3W, 19 .. 27),
1237    word_to_num(L3W, L3),
1238    Parts = [L3,L2,L1].
1239
1240split_vaddr(VA, BlockSizeBits, [VPN, Offset]) :-
1241    assert_word(VAW, 48),
1242    word_to_num(VAW, VA),
1243    subword(VAW, OffsetW, 1 .. BlockSizeBits),
1244    word_to_num(OffsetW, Offset),
1245    VPNWStart is BlockSizeBits + 1,
1246    subword(VAW, VPNW, VPNWStart .. 48),
1247    word_to_num(VPNW, VPN).
1248
1249test_split_vpn :-
1250    VA = 16'40201, % hex(1 | 1<<9 | 1<<18)
1251    split_vpn(VA, [1,1,1]).
1252
1253pt_alloc(Idx, Idx) :-
1254    not(pt(Idx,_,_)).
1255
1256pt_alloc(Try, Idx) :-
1257    NextTry is Try + 1,
1258    pt_alloc(NextTry, Idx).
1259
1260:- export pt_alloc/1.
1261pt_alloc(Idx) :-
1262    pt_alloc(0,Idx), !,
1263    assert(pt(Idx,-1,-1)).
1264
1265% Inner function for pt_delta, better not use anywhere else.
1266pt_delta_append_if_new(In, Out, pt(A,B,C)) :-
1267    nonvar(A), nonvar(B),
1268    % C magic: nonvar, or already installed, or it will be new allocated
1269    (nonvar(C) ; (pt(A,B,C) ; pt_alloc(C))),
1270    (pt(A,B,C) -> In = Out ; (Out = [pt(A,B,C) | In])).
1271
1272% Get the difference between the PT facts (which reflect in memory contents)
1273% and the newly to be inserted block translate facts.
1274% BlockDelta :: [(VPN, PPN), ...]
1275% PtDelta :: [pt(Idx, Offset, NextIdx), ...]
1276:- export pt_delta/3.
1277pt_delta(NodeId, BlockDelta, PtDelta) :-
1278    %findall((VPN, PPN), node_block_conf(NodeId, VPN, PPN), Maps),
1279    (foreach((VPN,PPN), BlockDelta),param(NodeId),fromto([], In, Out, PtDelta) do
1280        node_pt(NodeId, RootPt, _),
1281        split_vpn(VPN, [L3,L2,L1]),
1282        pt_delta_append_if_new(In,Out1,   pt(RootPt, L3, L2Idx)),
1283        pt_delta_append_if_new(Out1,Out2, pt(L2Idx, L2, L1Idx)),
1284        pt_delta_append_if_new(Out2,Out,  pt(L1Idx, L1, PPN))
1285    ).
1286
1287:- export test_pt_delta/1.
1288test_pt_delta(PtDelta) :-
1289    pt_alloc(Root),
1290    assert(node_pt([],Root,["OUT"])),
1291    pt_delta([], [(0,23)], PtDelta).
1292    % PtDelta = [pt(2, 0, 23), pt(1, 0, 2), pt(0, 0, 1)] modulo reordering.
1293
1294% For a list of PTs, assert the facts.
1295:- export assert_pt_delta/1.
1296assert_pt_delta(PtDelta) :-
1297    (foreach(X, PtDelta) do
1298        assert(X)
1299    ).
1300
1301:- export route_node_ids/2.
1302route_node_ids([], []).
1303route_node_ids([(NodeId,_) | As], Res) :-
1304    route_node_ids(As,Res1),
1305    union([NodeId], Res1, Res).
1306
1307:- export route_conf_for_id/3.
1308route_conf_for_id([], _, []).
1309route_conf_for_id([(NodeId,Conf) | As], NodeId, Res) :-
1310    route_conf_for_id(As, NodeId, Res1),
1311    union([Conf],Res1, Res).
1312
1313route_conf_for_id([_ | As], NodeId, Res) :-
1314    route_conf_for_id(As, NodeId, Res).
1315
1316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1317%%%% Big tests
1318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319:- export test_all/0.
1320test_all :-
1321    init, add_pci, add_process,
1322    BUFFER_SIZE = [memory, [1024]],
1323    ProcId = ["OUT", "PROC0", "PROC0"],
1324    Proc = region{node_id: ProcId},
1325    PciId = ["OUT", "PCI0", "PCI0"],
1326    Pci = region{node_id: PciId},
1327    common_free_buffer(BUFFER_SIZE, Proc, Pci, Resolved, Route),
1328    writeln(("Free buffer reachable from Proc and Pci found", Resolved)),
1329    writeln(("View from PCI ", Pci)),
1330    writeln(("View from Proc ", Proc)),
1331    writeln(("Using Block-Configuration ", Route)),
1332    route_node_ids(Route, NodeIds),
1333    (foreach(NodeId, NodeIds),param(Route) do
1334        route_conf_for_id(Route, NodeId, BlockDelta),
1335        pt_delta(NodeId, BlockDelta, PtDelta),
1336        writeln(("New PT entries for ", NodeId, " -> ", PtDelta))
1337    ),
1338    writeln("Installing Route"),
1339    install_route(Route),
1340    !, % make sure not to back track past the state modifying install_route
1341    region_base_name(Proc,ProcBaseAddr), % The first byte of the common region.
1342    PciBaseAddr = name{node_id:PciId},
1343    change_view(ProcBaseAddr, PciBaseAddr),
1344    writeln(("Lookup from Proc to PCI BaseAddr", PciBaseAddr)).
1345
1346:- export test_mem_if/0.
1347test_mem_if :-
1348    add_pci_alloc(addr(0,1,2)),
1349    pci_address_node_id(addr(0,1,2), PciEnum),
1350    add_process_alloc(proc(0)),
1351    process_node_id(proc(0), ProcEnum),
1352    alloc_common(21, PciEnum, ProcEnum).
1353
1354
1355:- export dec_net_debug/0.
1356dec_net_debug :- listing.
1357