From e1620c68b2bfcff7ad1bc8ce665cf4ce29452141 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 22 Feb 2022 16:15:14 +0100 Subject: [PATCH 01/35] Fix Briey simulation floating rxd blocking the uart #238 --- src/test/cpp/briey/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/cpp/briey/main.cpp b/src/test/cpp/briey/main.cpp index d2b372c5a..c0e165a22 100644 --- a/src/test/cpp/briey/main.cpp +++ b/src/test/cpp/briey/main.cpp @@ -382,6 +382,8 @@ class BrieyWorkspace : public Workspace{ timeProcesses.push_back(asyncReset); timeProcesses.push_back(jtag); timeProcesses.push_back(uartRx); + top->io_uart_rxd = 1; + SdramConfig *sdramConfig = new SdramConfig( 2, //byteCount From 32a5206541701278fdefcb00704269788ea23a92 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 4 Apr 2022 16:37:43 +0200 Subject: [PATCH 02/35] Update to latest risc-v-formal --- src/main/scala/vexriscv/plugin/FormalPlugin.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/vexriscv/plugin/FormalPlugin.scala b/src/main/scala/vexriscv/plugin/FormalPlugin.scala index a02218da0..2d70ebd8e 100644 --- a/src/main/scala/vexriscv/plugin/FormalPlugin.scala +++ b/src/main/scala/vexriscv/plugin/FormalPlugin.scala @@ -35,6 +35,8 @@ case class RvfiPort() extends Bundle with IMasterSlave { val trap = Bool val halt = Bool val intr = Bool + val mode = Bits(2 bits) + val ixl = Bits(2 bits) val rs1 = RvfiPortRsRead() val rs2 = RvfiPortRsRead() val rd = RvfiPortRsWrite() @@ -91,6 +93,8 @@ class FormalPlugin extends Plugin[VexRiscv]{ rvfi.trap := False rvfi.halt := False rvfi.intr := False + rvfi.mode := 3 + rvfi.ixl := 1 // rvfi.rs1.addr := output(INSTRUCTION)(rs1Range).asUInt // rvfi.rs2.addr := output(INSTRUCTION)(rs2Range).asUInt // rvfi.rs1.rdata := output(RS1) From 53d52692de38633a6b3b58dd1e297d206ad0bd08 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 8 Apr 2022 11:09:48 +0200 Subject: [PATCH 03/35] #240 Code generation now warn against cpu generation without illegal instruction catch and ebreak being disabled, as it may make crash some software, ex : rust --- src/main/scala/vexriscv/plugin/CsrPlugin.scala | 4 ++++ src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index bd48928c4..5ba152331 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -532,6 +532,10 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep override def setup(pipeline: VexRiscv): Unit = { import pipeline.config._ + if(!config.ebreakGen) { + SpinalWarning("This VexRiscv configuration is set without software ebreak instruction support. Some software may rely on it (ex: Rust). (This isn't related to JTAG ebreak)") + } + csrMapping = new CsrMapping() inWfi = False.addTag(Verilator.public) diff --git a/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala index 71e389482..8a458ff38 100644 --- a/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala @@ -77,6 +77,9 @@ class DecoderSimplePlugin(catchIllegalInstruction : Boolean = false, override def setup(pipeline: VexRiscv): Unit = { + if(!catchIllegalInstruction) { + SpinalWarning("This VexRiscv configuration is set without illegal instruction catch support. Some software may rely on it (ex: Rust)") + } if(catchIllegalInstruction) { val exceptionService = pipeline.plugins.filter(_.isInstanceOf[ExceptionService]).head.asInstanceOf[ExceptionService] decodeExceptionPort = exceptionService.newExceptionPort(pipeline.decode).setName("decodeExceptionPort") From bd74833900d6b0e01c1478b3da91878281487dd5 Mon Sep 17 00:00:00 2001 From: Sallar Ahmadi-Pour Date: Mon, 25 Apr 2022 12:21:41 +0200 Subject: [PATCH 04/35] add murax peripheral extension tutorial --- doc/gcdPeripheral/README.md | 592 ++++++++++++++++++ .../murax-gcd-diagrams-gcd-controlpath.png | Bin 0 -> 68613 bytes .../img/murax-gcd-diagrams-gcd-datapath.png | Bin 0 -> 56769 bytes .../img/murax-gcd-diagrams-gcd-dp+cp.png | Bin 0 -> 27013 bytes .../img/murax-gcd-diagrams-gcd.png | Bin 0 -> 15632 bytes .../img/murax-gcd-diagrams.drawio | 1 + doc/gcdPeripheral/img/simulationWave.PNG | Bin 0 -> 48129 bytes .../src/main/c/murax/gcd_world/makefile | 134 ++++ .../murax/gcd_world/project/build.properties | 1 + .../src/main/c/murax/gcd_world/src/crt.S | 98 +++ .../src/main/c/murax/gcd_world/src/gcd.h | 13 + .../src/main/c/murax/gcd_world/src/gpio.h | 15 + .../main/c/murax/gcd_world/src/interrupt.h | 17 + .../src/main/c/murax/gcd_world/src/linker.ld | 110 ++++ .../src/main/c/murax/gcd_world/src/main.c | 62 ++ .../src/main/c/murax/gcd_world/src/main.h | 78 +++ .../src/main/c/murax/gcd_world/src/murax.h | 20 + .../main/c/murax/gcd_world/src/prescaler.h | 16 + .../src/main/c/murax/gcd_world/src/timer.h | 20 + .../src/main/c/murax/gcd_world/src/uart.h | 42 ++ .../src/main/scala/vexriscv/demo/Murax.scala | 559 +++++++++++++++++ .../vexriscv/periph/gcd/Apb3GCDCtrl.scala | 39 ++ .../scala/vexriscv/periph/gcd/GCDCtrl.scala | 68 ++ .../scala/vexriscv/periph/gcd/GCDData.scala | 54 ++ .../scala/vexriscv/periph/gcd/GCDTop.scala | 46 ++ .../scala/vexriscv/periph/gcd/GCDTopSim.scala | 52 ++ 26 files changed, 2037 insertions(+) create mode 100644 doc/gcdPeripheral/README.md create mode 100644 doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-controlpath.png create mode 100644 doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-datapath.png create mode 100644 doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-dp+cp.png create mode 100644 doc/gcdPeripheral/img/murax-gcd-diagrams-gcd.png create mode 100644 doc/gcdPeripheral/img/murax-gcd-diagrams.drawio create mode 100644 doc/gcdPeripheral/img/simulationWave.PNG create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/makefile create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/project/build.properties create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/crt.S create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gcd.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gpio.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/interrupt.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/linker.ld create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.c create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/murax.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/prescaler.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/timer.h create mode 100644 doc/gcdPeripheral/src/main/c/murax/gcd_world/src/uart.h create mode 100644 doc/gcdPeripheral/src/main/scala/vexriscv/demo/Murax.scala create mode 100644 doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/Apb3GCDCtrl.scala create mode 100644 doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDCtrl.scala create mode 100644 doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDData.scala create mode 100644 doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTop.scala create mode 100644 doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTopSim.scala diff --git a/doc/gcdPeripheral/README.md b/doc/gcdPeripheral/README.md new file mode 100644 index 000000000..210fbbafb --- /dev/null +++ b/doc/gcdPeripheral/README.md @@ -0,0 +1,592 @@ +# Tutorial on Implementing a Peripheral for the VexRiscv Based Murax SoC +**By** + +**Sallar Ahmadi-Pour - Researcher, University of Bremen, Group of Computer Architecture** + +[http://www.informatik.uni-bremen.de/agra/projects/risc-v/](http://www.informatik.uni-bremen.de/agra/projects/risc-v/) + +[http://www.informatik.uni-bremen.de/agra/](http://www.informatik.uni-bremen.de/agra/) + + +## 1. Introduction +Traditional hardware design often requires using languages like VHDL and Verilog and tooling that don't catch errors that can be caught with static analysis of the design. Additionally, information developers receive from the tools is scarce and often lead inexperienced developers on an odyssey. Currently emerging tools (Verilator, Yosys, etc.) for hardware design and languages for hardware description (SpinalHDL, Amaranth, etc.) tackle these and other existing issues. + +Projects like SpinalHDL and the thereon based highly configurable VexRiscv processor experience a rise in popularity and usage amongst academic and commercial users. The increased popularity also requires an increase in educational resources. Due to the specific popularity in the academic environment it only seems natural that researchers document their approaches and insights (not only in peer reviewed publications in a journal). This will allow the next generation of hardware designers to extend and explore big projects like VexRiscv. + +## 2. Our Goal for this Tutorial +Murax SoC is a VexRiscv configuration that is a very lightweight RISC-V platform. +It features a basic set of peripherals (UART, GPIO, Prescalers and Timers) around a pipelined memory bus and Apb3 peripheral bus. +The Murax SoC features enough to more than a toy system and being small and thus offering space for extension. + +For the choice of possible algorithms, that we want to describe in hardware rather than software, the algorithm for calculating the Greatest Common Divisor (GCD) is a good example to start off. There are many digital design resources available on designing a GCD module. + +We will add the hardware peripheral module to the Murax on the Apb3 bus with memory mapped registers to control the module and transfer the data around for the calculation. +In this way we transfer the resources the software to the hardware implementation. +The aspects we will shed some light upon will be + +a) How do we implement an algorithm that we know from the software domain in a hardware implementation suited for FPGAs? + +b) How do we prepare and integrate a new peripheral into the Murax domain and map its control and data ports via memory mapped registers? + +c) How do we extend the software to use the peripheral easily in our baremetal code? + +For a) we will start off the pseudocode of the GCD and work our way to a hardware implementation in SpinalHDL. +We will evaluate that design in a SpinalHDL testbench with Verilator as the simulation backend and drive the testbench with randomly generated values which we compare to a software implementation of the same algorithm. +For b) we will look into the features of SpinalHDL and the structure of the Murax SoC to get an idea where and how to integrate our peripheral. +Before adding the peripheral into the Murax we also need to decide on the details of memory mapping our control and data ports to memory mapped registers (i.e, addresses, write/read/clear modes, etc.). + +At the end there is a small list of possible extensions from which anyone can continue with their own additions. + +## 3. GCD HW Implementation +Let us start the HW implementation by looking at some kind of specification. + +```c +// Pseudocode of the Euclids algorithm for calculating the GCD +inputs: [a, b] +outputs: [ready, a] +ready := False +while(!ready): + if(a > b): + a := a - b + else if(b > a): + b := b - a + else: + ready := True +``` + +The pseudocode shows the GCD algorithm we want to implement in hardware. +Implementing algorithms in hardware in the Register Transfer Level (RTL) style will require you to separate the control path (so if, else, while, for) and the data path (moving, calculating and comparing data). +Inevitably results from data and comparisons affect the control flow and the control flow affects the data flow. +Thus the two paths need to communicate the shared information. +But let us start at defining the interface of our module that will calculate the GCD. + +![GCD Top Diagram](./img/murax-gcd-diagrams-gcd.png) + +Our pseudocode already defines some in- and outputs that can aid us in defining the interface for our module. +At this point we don't want to think about which bus we connect our module to (APB, AXI, Wishbone, etc.). +We take care about that part later. +We simply know we have our input integers A and B, a signal to indicate the start of the calculation, the result and a signal indicating the completion of the calculation. +We choose 32 bit integers and use a valid-ready mechanism (we add a valid signal to kick of the calculation). +The interface features the values A, B and result as the data signals, valid and ready are control signals. +Signals for reset and clock are omitted for readability (unless explicitly used these are handled by SpinalHDL internally anyways). + +From this top level perspective we can describe the behavior as follows: Once we apply a set of operands A and B and then apply the valid signal the module calculates the GCD for a variable amount of clock cycles. +We know the result is ready and can be read once the ready signal is asserted. +Inside the GCD module we will have two other modules: the data path GCDData and the control path GCDCtrl. +We notice again, the data signals (opA, opB and result) belong to our data path and the control signals (valid and ready) belong to our control path. + +![GCD top level block diagram](./img/murax-gcd-diagrams-gcd-dp+cp.png) + +The data path will consist of some basic RTL blocks like multiplexers, a subtraction, comparators and registers. +The elements are connected and arranged such that they represent the dataflow of the algorithm. +Parts of the data path are enabled by the control path. +The control path will be represented by a Finite State Machine (FSM), which orchestrates the data paths calculation of the result. + +![GCD data path](./img/murax-gcd-diagrams-gcd-datapath.png) + +The diagram of the data path shows the processing elements for our algorithm in hardware, with their control input and outputs respectively. +From this we can already see what the interface towards the control path looks like. +The control path needs to know the results of the comparisons. +Vice versa the data path gets controlled through selecting the subtract operands (or more precisely their order), the register enables and an initiation signal for a defined start state. +In the data path, the D-Flipflops (DFF) hold the values A and B that are used for the calculation and they change value throughout the computation. +A subtraction which is set up for a computation such that `r = x - y` with x being the "left" and y being the "right" operand. +The left and right operands are multiplexed from our control path inputs. +Two comparators compute the greater than (cmpAgtB) and less than (cmpAltB) operation. +The result, the GCD of A and B, will be available in the A register after the calculation is done. +Completion of the calculation is signaled by the control path. + +![GCD control path](./img/murax-gcd-diagrams-gcd-controlpath.png) + +In the diagram of the control path we see the same interface (with inverse directions — this information will be helpful later in SpinalHDL). +The interface of the control path are the top level valid signal, the ready signal indicating the finished computation, the results of the two comparisons `A > B` (*cmpAgtB*) and `B > A` (*cmpAltB*). +Initially the FSM is in an idle state, waiting for the valid signal to be asserted, on exit of this state, the init signal is set to 1 to clock in the values of A and B into their respective registers. +Similar to the pseudocode the FSM loops for the calculation and based on the comparators of the data path and orchestrates the data path to calculate either `a := a - b` or `b := b - a`. +If both if the comparators outputs are 0, the end of the calculation is reached. +Within the `calcDone` state the `ready` signal is set to 1. +With the entry of the `idle` state the module becomes ready to calculate another GCD. +The control path drives all the outputs based on the state in the state machine (Moore FSM). +The guards on the transitions show the condition with which the respective transition occurs. +These block diagrams, digital logic and the FSM can be quickly implemented in SpinalHDL, things like the `DataControlIF` that interconnect between the data path and control path can be quickly created and connected in SpinalHDL as well. + +## 4. SpinalHDL implementation +First we can take a look at the interface between the data and control path. + +```scala +// in GCDTop.scala +case class GCDDataControl() extends Bundle with IMasterSlave{ + val cmpAgtB = Bool + val cmpAltB = Bool + val loadA = Bool + val loadB = Bool + val init = Bool + val selL = Bool + val selR = Bool + + override def asMaster(): Unit = { + out(loadA, loadB, selL, selR, init) + in(cmpAgtB, cmpAltB) + } +} +``` + +We can define a Bundle that implements the `IMasterSlave` Interface (see the [Bundle documentation](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Data%20types/bundle.html?highlight=master%20slave#master-slave)), which allows us to use a operator (`<>`) to interconnect modules and their signals without explicitly describing each wire and connection (other than inside the Bundle from above). +In the Bundle we can define the signals with their types. +We override the `asMaster()` Method (line 10 to 13) from the `IMasterSlave` interface. +In the `asMaster()` Method we define the signal direction from the point of view of the control path. +Thus `cmpAgtB` and `cmpAltB` are inputs and `loadA`, `loadB`, `selL`, `selR`, `init` are outputs. +SpinalHDL will infer the directions for the data path side when we will use the `<>`-Operator. +With that our top level module will look very tidy: + +```scala +// in GCDTop.scala +class GCDTop() extends Component { + val io = new Bundle { + val valid = in Bool() + val ready = out Bool() + val a = in(UInt(32 bits)) + val b = in(UInt(32 bits)) + val res = out(UInt(32 bits)) + } + val gcdCtr = new GCDCtrl() + gcdCtr.io.valid := io.valid + io.ready := gcdCtr.io.ready + val gcdDat = new GCDData() + gcdDat.io.a := io.a + gcdDat.io.b := io.b + io.res := gcdDat.io.res + gcdCtr.io.dataCtrl <> gcdDat.io.dataCtrl +} +``` + +Lines 2 to 8 define the input/output Bundle inline, lines 9 and 12 instantiate the control and data path. All other lines are interconnecting the IO signals. Note in line 16 we interconnect the control and data path by using the `<>`-Operator as they use the shared interface description from earlier as a input (called `dataCtrl` in the design). We will see this in the respective modules input/output bundles. + +Our data path in SpinalHDL looks like this: + +```scala +// in GCDData.scala +class GCDData() extends Component { + val io = new Bundle { + val a = in(UInt(32 bits)) + val b = in(UInt(32 bits)) + val res = out(UInt(32 bits)) + val dataCtrl = slave(GCDDataControl()) + } + //registers + val regA = Reg(UInt(32 bits)) init(0) + val regB = Reg(UInt(32 bits)) init(0) + // compare + val xGTy = regA > regB + val xLTy = regA < regB + // mux + val chX = io.dataCtrl.selL ? regB | regA + val chY = io.dataCtrl.selR ? regB | regA + // subtract + val subXY = chX - chY + // load logic + when(io.dataCtrl.init){ + regA := io.a + regB := io.b + } + when(io.dataCtrl.loadA){ + regA := subXY + } + when(io.dataCtrl.loadB){ + regB := subXY + } + io.dataCtrl.cmpAgtB := xGTy + io.dataCtrl.cmpAltB := xLTy + io.res := regA +} +``` + +Lines 2 to 7 show the Bundle for the IO signals. Note the signal in line 6 (`dataCtrl`), we use the defined Bundle from earlier and give it the direction `slave()` instead `in()` or `out()`. +This tells SpinalHDL to infer the directions of the Bundle signals according to the `asMaster()` method (in that case the inverse directions). +We will see this again in the control path. +The rest of the module (or components, thats how SpinalHDL modules are called) consists of defining signals, registers, and behavior. +Registers can be defined through a `Reg()` components that takes a type and optionally a reset value (via `init()`). +We can write to the register in our `when()` Blocks which could be interpreted as the enable signals for the registers. +(* Side note: technically we describe a multiplexing onto each register as we have multiple cases of enables and different data sources, but we can abstract from that in SpinalHDL a bit and keep it in the back of our minds*). + +Now for the control path of our GCD module: +```scala +// in GCDCtrl.scala +class GCDCtrl() extends Component { + val io = new Bundle { + val valid = in Bool() + val ready = out Bool() + val dataCtrl = master(GCDDataControl()) + } + val fsm = new StateMachine{ + io.dataCtrl.loadA := False + io.dataCtrl.loadB := False + io.dataCtrl.init := False + io.dataCtrl.selL := False + io.dataCtrl.selR := False + io.ready := False + val idle : State = new State with EntryPoint{ + whenIsActive{ + when(io.valid){ + io.dataCtrl.init := True + goto(calculate) + } + } + } + val calculate : State = new State{ + whenIsActive{ + when(io.dataCtrl.cmpAgtB){ + goto(calcA) + }.elsewhen(io.dataCtrl.cmpAltB){ + goto(calcB) + }.elsewhen(!io.dataCtrl.cmpAgtB & !io.dataCtrl.cmpAgtB){ + goto(calcDone) + } + } + } + val calcA : State = new State{ + whenIsActive{ + io.dataCtrl.selR := True + io.dataCtrl.loadA := True + goto(calculate) + } + } + val calcB : State = new State{ + whenIsActive{ + io.dataCtrl.selL := True + io.dataCtrl.loadB := True + goto(calculate) + } + } + val calcDone : State = new State{ + whenIsActive{ + io.ready := True + goto(idle) + } + } + } +} +``` + +The lines 2 to 6 show the input/output signals again, and this time the `dataCtrl` signal, at line 5, shows the direction as `master()`. +This will apply the directions that we set in the first code snipped. +SpinalHDL offers a library to build FSMs and since this module is only that, our control path is descriptive. +We set default values for outputs (lines 8 to 13) and apply the according value in the respective state. + +The API for FSMs in SpinalHDL offers much more than we use here. +In each state we can describe actions for `onEntry`, `onExit`, `whenIsNext` and for `whenIsActive` phases (see the [State Machine documentation](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Libraries/fsm.html)). +The `onEntry` phase refers to the cycle before entering the state, `onExit` will be executed if the next cycle will be in a different state, and `whenIsNext` will be executed if the state machine will be in that state in the next cycle. +That resembles the capabilities of FSM we have in UML/SysML or in StateCharts. +There is also the possibility to nest FSMs hierarchically or have delay states for a certain amount of cycles. +Describing these things in classic HDL is a lot of boilerplate that SpinalHDL can generate for us instead. + +But with these modules we can already run some first simulations, testing our design for functionality. +And as traditional HDLs go we need a testbench for this. +This applies to SpinalHDL as well. +The default way for [simulation in SpinalHDL](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Simulation/index.html) is by writing a testbench with SpinalHDL and Scala and then getting it simulated through Verilator. +Verilator compiles our HDL (generated from SpinalHDL) to a C++ simulation model, our testbench interacts with that and thus we can have a fast simulation at hand. +Lets jump straight into the simulation testbench and see how SpinalHDL aids our work here: + +```scala +// in GCDTopSim.scala +object GCDTopSim { + def main(args: Array[String]) { + SimConfig.doSim(new GCDTop()){dut => + def gcd(a: Long,b: Long): Long = { + if(b==0) a else gcd(b, a%b) + } + def RndNextUInt32(): Long = { + ThreadLocalRandom.current().nextLong(Math.pow(2, 32).toLong - 1) + } + var a = 0L + var b = 0L + var model = 0L + dut.io.a #= 0 + dut.io.b #= 0 + dut.io.valid #= false + + dut.clockDomain.forkStimulus(period = 10) + dut.clockDomain.waitRisingEdge() + + for(idx <- 0 to 50000){ + a = RndNextUInt32() + b = RndNextUInt32() + model = gcd(a,b) + dut.io.a #= a + dut.io.b #= b + dut.io.valid #= true + dut.clockDomain.waitRisingEdge() + dut.io.valid #= false + waitUntil(dut.io.ready.toBoolean) + assert( + assertion = (dut.io.res.toBigInt == model), + message = "test " + idx + " failed. Expected " + model + ", retrieved: " + dut.io.res.toBigInt + ) + waitUntil(!dut.io.ready.toBoolean) + } + } + } +} +``` + +In line 3 we basically setup our Design Under Test (DUT), and we could setup some other simulations options like generating the VCD wavetrace. +We want to generate some arbitrary amount of testcases and compare the results against a (different) implementation of the GCD algorithm in software. +Doing this for enough random cases can give confidence in the design, tho it will not always cover edge cases and other aspects that are covered by a constrained random approach, white box testing or formal methods to verify our design. +Lines 4 to 6 are our (recursive) software implementation. +Lines 7 to 9 generate a random number in the range of a UInt32 — we have to do this by hand because of the nature of Java, Scala and SpinalHDL and how they interact with each other when it comes to numeric values and types. +Lines 10 to 15 setup our input for the DUT and in line 17 and 18 we set up the clock and trigger the first event for our signals to be applied to the inputs. +Lines 20 to 35 describe the application of 50k random integers to our design, and our software model, and then comparing them after we waited for the hardware cycles to pass. +We use the `assert` to output a message to the terminal in case a testcase doesn't match with the software model. +If we add `.withWave` to the line 3 we can obtain a wavetrace (tho its recommended not to run as many testcases, as the dump will be huge otherwise). + +![GCD wave trace](./img/simulationWave.PNG) + +## 5. GCD Murax Integration +Now that we have a standalone module that we want to integrate into the Murax SoC. + +Since the Murax is using the APB bus for the peripherals, our module needs to map the IO signals into the memory mapped space of the APB bus. + +```scala +// in Apb3GCDCtrl.scala +object Apb3GCDCtrl { + def getApb3Config = Apb3Config( + addressWidth = 5, + dataWidth = 32, + selWidth = 1, + useSlaveError = false + ) +} + +class Apb3GCDCtrl(apb3Config : Apb3Config) extends Component { + val io = new Bundle { + val apb = slave(Apb3(Apb3GCDCtrl.getApb3Config)) + } + val gcdCtrl = new GCDTop() + val apbCtrl = Apb3SlaveFactory(io.apb) + apbCtrl.driveAndRead(gcdCtrl.io.a, address=0) + apbCtrl.driveAndRead(gcdCtrl.io.b, address=4) + val resSyncBuf = RegNextWhen(gcdCtrl.io.res, gcdCtrl.io.ready) + apbCtrl.read(resSyncBuf, address=8) + apbCtrl.onRead(8)(resSyncBuf := 0) + apbCtrl.onRead(8)(rdySyncBuf := False) + val rdySyncBuf = RegNextWhen(gcdCtrl.io.ready, gcdCtrl.io.ready) + apbCtrl.read(rdySyncBuf, address=12) + gcdCtrl.io.valid := apbCtrl.setOnSet(RegNext(False) init(False), address=16, 0) +} +``` + +Looking at the other peripherals in the Murax, we get an idea how to implement our own Apb3 Mapping (this is also part of the SpinalHDL Workshop). + +The components uses the APB3 Bus as a slave peripheral. +In line 14 we create a instance of our GCD module, in line 15 we create a [APB3 Slave Factory](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Libraries/bus_slave_factory.html) (for our APB bus connection of the component). +This factory offers us to add memory mapped registers very easily that create all the logic needed to interconnect with our module properly. +A register which can be read and written to can be seen in line 16 and 17 (`driveAndRead()`). +We pass the signal we want to be buffered through that register and an address. +Our result is [buffered with a `RegNextWhen`](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Sequential%20logic/registers.html#instantiation) (which buffers the first argument `gcdCtrl.io.res` based on the enable signal that is the second argument `gcdCtrl.io.ready`). +We need this because our result is visible for the clock cycle that the ready signal is asserted true by the control path. +We do something similar with the ready signal, and keep it buffered for longer than just one clock cycle (since we don't know when the software will check these registers). +The result and ready registers will be read-only (`read()`) on their respective addresses. +If the result is read (even if ready was not checked) we will flush both registers as if we fetched the result and don't need it anymore. +The valid signal shouldn't be asserted longer than one clock cycle, this is achieved in line 24. +We use a register that sets itself to 0/false whenever its written to. +So if we write a 1/true into it, after one cycle its set to 0/false again. + +| Address | Name | Description | Mode | +|---------|-------|----------------------------------------------|----------------------------------| +| 0 | a | Operand A of the GCD(a,b) | R/W | +| 4 | b | Operand B of the GCD(a,b) | R/W | +| 8 | res | Result of GCD(a,b) | RO, clears res and ready on read | +| 12 | ready | Ready, 1 if result available, 0 otherwise | RO | +| 16 | valid | Valid, write 1 to start calculating GCD(a,b) | WO, clear after write | + + +In this way we implemented this memory mapped register bank with various modes. +Now all thats left is to attach our module to the APB bus of the Murax SoC and write some bare metal firmware to access it. + +We created our modules inside the VexRiscv structure as follows: +``` +src/main/scala/ +├── spinal +└── vexriscv + ├── demo + ├── ip + ├── periph <--- we add this directory with subdir + │ └── gcd + │ ├── Apb3GCDCtrl.scala + │ ├── GCDCtrl.scala + │ ├── GCDData.scala + │ ├── GCDTop.scala + │ └── GCDTopSim.scala + ├── plugin + └── test + +``` + +To integrate our `Apb3GCDCtrl` peripheral into the Murax we need to modify the Murax SoC (`src/main/scala/vexriscv/demo/Murax.scala`) directly. +Deep in the source there will be a comment designating the start of the APB peripherals (`//******** APB peripherals *********`). +There we are going to add our peripheral and designate some memory mapped space to it. + +This step is straightforward as we can add the peripheral similarly to the existing ones. +After the code for the timer `MuraxApb3Timer` module we add our GCD peripheral: + +```scala +val gcd = new Apb3GCDCtrl( + apb3Config = Apb3Config( + addressWidth = 20, + dataWidth = 32 + ) +) +apbMapping += gcd.io.apb -> (0x30000, 1 kB) +``` + +And thats it! + +The Murax SoC now supports our own GCD peripheral. +All thats left now is to use the peripheral in a piece of software. + +## 6. Software Driver Integration + +We start off the software part with the existing `hello_world` example and copy it into a new directory `gcd_world`. + +Since we support a new peripheral in hardware we also need to support it from the software (its supported but we are making it more usable for the developer). +We add a new file in the `gcd_world/src` directory called `gcd.h`. + +```c +// in gcd.h +#ifndef GCD_H_ +#define GCD_H_ + +typedef struct +{ + volatile uint32_t A; + volatile uint32_t B; + volatile uint32_t RES; + volatile uint32_t READY; + volatile uint32_t VALID; +} Gcd_Reg; + +#endif /* GCD_H_ */ + +``` + +With that we define the available memory mapped registers starting from the base address of the peripheral. + +We then edit the `murax.h` header file in the same directory: + +```c +#ifndef __MURAX_H__ +#define __MURAX_H__ + +#include "timer.h" +#include "prescaler.h" +#include "interrupt.h" +#include "gpio.h" +#include "uart.h" +#include "gcd.h" + +#define GPIO_A ((Gpio_Reg*)(0xF0000000)) +#define TIMER_PRESCALER ((Prescaler_Reg*)0xF0020000) +#define TIMER_INTERRUPT ((InterruptCtrl_Reg*)0xF0020010) +#define TIMER_A ((Timer_Reg*)0xF0020040) +#define TIMER_B ((Timer_Reg*)0xF0020050) +#define UART ((Uart_Reg*)(0xF0010000)) +#define GCD ((Gcd_Reg*)(0xF0030000)) + + +#endif /* __MURAX_H__ */ +``` + +Our addition is the line `#define GCD ((Gcd_Reg*)(0xF0030000))`. +With that we create a way of accessing the memory mapped registers without directly referring to the peripherals address (`0xF0030000`) or having to calculate offsets for the registers. + +Now we can start writing our software! + +In our `main.c` we add a function to make the peripheral handling a bit more convenient: + +```c +uint32_t gcd(uint32_t a, uint32_t b){ + GCD->A = a; + GCD->B = b; + GCD->VALID = 0x00000001; + uint32_t rdyFlag = 0; + do{ + rdyFlag = GCD->READY; + }while(!rdyFlag); + return GCD->RES; +} +``` + +This function will take the parameters `a` and `b` and applies them to the respective hardware registers `A` and `B` of our peripheral. +Then the `VALID` signal is set (our Apb3 wrapper takes care of setting it back to 0). +All thats left is waiting for the result, which is done by polling the ready flag until its available and then returning our result value `RES`. + +The software contains a little more code for formatting numbers to print them onto the UART device but reading and understanding that is left as an exercise to the reader. + +So how do we execute our software on the Murax now? + +First we compile the software with the make file. For that call `make` inside `src/main/c/murax/gcd_world`. +You should get some minor warnings and a statistics about the memory usage like + +``` +Memory region Used Size Region Size %age Used + RAM: 1752 B 2 KB 85.55% +``` + +Now we can edit the `Murax.scala` one last time before we execute our simulation. +For this scroll down in the `Murax.scala` file until `MuraxWithRamInit`. +In order to load the memory with our new software instead of the `hello_world` example we edit this part. + +```scala +object MuraxWithRamInit { + def main(args: Array[String]) { + SpinalVerilog( + Murax( + MuraxConfig.default.copy( + onChipRamSize = 4 kB, + onChipRamHexFile = "src/main/c/murax/gcd_world/build/gcd_world.hex" + ) + ) + ) + } +} +``` + + + +Then in the root directory we open `sbt` and call `runMain vexriscv.demo.MuraxWithRamInit` or we call `sbt "runMain vexriscv.demo.MuraxWithRamInit"` directly. + +This will call SpinalHDL to generate the modified Murax SoC with our small software example. + +The last thing we need to do is call the simulation. +For that navigate to `src/test/cpp/murax` and call `make clean run`. + +After some time you should see the following output in the terminal: + +``` +... +BOOT +hello gcd world +gcd(1,123913): +1 +gcd(461952,116298): +18 +gcd(461952,1162): +2 +gcd(461952,11623): +1 +``` + +Keep in mind that we are simulating a SoC. There is no shutdown for our simulation so we have to stop it by ourselves by pressing `CTRL+C`! +Otherwise the simulation won't stop. + + + +## 7. Conclusion + +In a tutorial we described how to convert pseudocode for the GCD calculation into SpinalHDL based hardware. Furthermore the hardware was integrated into the VexRiscv based Murax SoC. +To demonstrate the usage an example C project was set up and the hardware peripheral was used from within the software. + +This tutorial covered the translation from RTL into SpinalHDL, writing a small wrapper for the APB3 bus used in the Murax SoC, integrating the peripheral into the Murax SoC with designated memory mapped space and writing software in C for the Murax SoC that uses the hardware peripheral to calculate the GCD and print it out on the UART of the Murax SoC. + +Now there are a few open challanges to approach as an exercise here are two that would follow up naturally to our existing code: + +* The Murax SoC features interrupts, we could stop polling our ready flag and instead trigger an interrupt from the `Apb3GCDCtrl` instead. +* Write the same algorithm in C and compare it with the hardware peripheral. Is it faster, is it smaller (interacting with the peripheral in software still costs instruction in terms of memory) \ No newline at end of file diff --git a/doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-controlpath.png b/doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-controlpath.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b09f6a0ce46e5e618becfb24fc771afffb3dcb GIT binary patch literal 68613 zcmeFZbx@S=`!7x^AyN`bcbBLjjdV$OE+JBaAi1<4C`z{oN;gY)Eg&VJNGyVM2rMm2 zEOBo9e1Gq^-`|-tb7syz=Z`b9%seo=`#kr3U-7zLuj}Hq)?+0if;$8l7#Kt<%JMoG z7}zKb49sD?Yv3<;%i~)yFc>jZ0thdpzfDO#C;Y!SdQNYzJAUUk8!Muo!SpUAPJh9G8G*7d}(R~-jSr{CZhrNgbdL0Bh>|MBW zFwQ?O=C>H(0bxYc?>FNy!X-IgsbQtVDokrw@Z&J>WB+*tui)|ixNmUy8b0eT=QMaz z8!_#UX!TBq&x*6GGGdbb^NKXXx`m1p_j2}C2NxL>{Ps2W{&NR#0i(ktfKC%N=83CA zT-O&4TRmzJ=;3HKmA}^YMKc&aduG#3xxpa=smMwybJsykJ2p%i|NE@}^Vi%$wN=}w z{QC|@%mea|Fp*2@%J5ULkWG8Ll8jrZx;xLjlK3x>DmMNA$I zYyV%lH68vq+jH##C5EMOo^xdh3`ggjK^7Of&t_Y=UXybqs~ZceRFWy=J?m)eTS>N}xtYmApD3#XF6>1aq)t?LP2Ve1x=F3h~ z2gT5cl(TAN>SYf9uDQi+pzPRUVD7#;v|dZs2eV(JKk-{2n5wpkF>7+uzBoUUR!P5C zWIGtAp_&wAY~l1fomDf=%+TELF5icCz0(&PJUy2D{JQPw*#&q9SLPh-01V>2*HeTb z1D+NFjWKjnffKnUIDllqN4)G1=nabFclD$=Q+z|mQJ1UwLDjl9x(w%{%(OoHVz$jm z++q5D{;}e#>%{bZ1q|;}`fWG{Tbqyc(pXi<)vA{Jm-q8{+z;B{Z@5-k_r&uDG%H7; zhH@|`!X%|e&dtgTN494gS%NGYxl;rzqEAi39Vu*gdtb6TRORwtU;lhaGg>0&YTWJ~ zFt)~}n-|ML#u^(%#Xs;<+yLrLIM58x{!+53s4YHShHt{c@7;xz4F)4xKpb zwJj+{m-rz7+LGZ|<0 zGh6x#8}iYL=D!zlRms_Sw_Lyw6wg5>!Bi$*n=D4;sFwc1LGOVNhx2reh7|f(c&Z&8 zY|@u12yJQ6L**4ZDiYE0=}Lc4g}7Fp3~z&jq-K$UxW1+Gc>f-_RAVoPb6F0In8wc{ z6-Sv-2H0q`K<&VULGr18_&xgJ!CbGYakmEGMg0;$mVIi37ITKd?dakm$jOSkgv+pI%} z8YiO7gOD#D#0>c!tDB1I9}ek&$A{2WJNJChr=`KEQpFj(5T?3t+5CdtI~&_^*` zo};sx==o5X=^3T3aFjc4ffLdYy5TGz3@q_FUha7DCs_TD$SP@8D`_mAl#2LgWzAe6 z=R?U9@j_~fF(mUA?aHjREMo-?t}BXPx)*d+Rz{ zgP!gj<;|3=;tS5!4V`9Nl0>F&6X~2oafcLD*sT5TbJb2nz-Wz&?z+&fxo2az2jxKUHg=L>XZ}kX z8(tY7r|+N6BlJi*8+BLl%ubyqEBLr{3lc#T;Wj*Xr3_+`{Be}XUCTYlur@rn(|*qX zaDQ0~`kPcZm=fw5cJdHER)!VC>X_Vb)~2oC;z+SCKT8)e{bGK~g$za#;vPQ0tq}bI)HzG5kC%j@Di>CdIKbp>mate3+@dB%- zwMeug&58zsrEeZ(GrbaY8Fi5kZku@TAORS{6@*Arif|ED1*w-)=>2j5tiE5S`aP=^ z1WUkHpgg)!PS009#rtu(vS;|mC#~ssossB`oQJxr1J?tjV`&?!2jhv<{Mn!AAz1b$ zndfa{(D8>+l--x`Z#CVWW$bbKO~F?_m2hl z$G;w~v48a-Bfm<`W8t$umIi0zg6Z(|f*N0CNIchRLTz&M%{}{6GFZ(%WMJU01gU5& zSSoB_2%P15zWDi@eu@Ws4m|K_#nH;(x}|E-e%^9*Be|aEk2wpUbzIM}4;B6IVM@0G z<(CRzM@fQ%zyaj3FbUAg)NFOy0ssiK<~k?Gz-OOe4{yf@2L9b zH|ch-kK?;dsIT)Z)+-;9? z6p%!Xu{dm>7}+A+tXf&sGQT*Pehs8;T>E993E82b5eD%R5e$}0bZtt}9Gv<$E1Grg z@K#cjFKSqM7YyB)(m!|=ntQt-Cilg6O{!C2SZ{xiRZZ#VSRk5S(YzXq((ixHSZgm#@K0~c~jc;b~G2uwpKywkm6e~No4dGsiSm`{7FMK$pyj<@-upNzH!dDkDRzZW!@qCczbjwUx< z4LHhM?oa8BgD3l9CYj))`H{h(flZ3d{u7TiHoJ|)(KaZwNT<+ba}2 zDV`ljUOUvH3^ZJia$RR!Ix3Tkwp{y20}DtSbX zY6#q3b5{sJ&4={mMWu{oW1%Kk^viVhuLVhP{-6)&9bi0*XcN(+3_1~2m8N91ifi{- zu+~E}qNGoq!#c2bQepZS6TY-ghVv((@k};;wJspcmwfRi&wVtz69(?{3+Sq?9rS5_y^2C&% zpDISRo_{tojNw9hLaf>Us)0iAuB`SGcJGsB5cx-5FZ?MKul-#eCIKvL>ic4AQiK4C ze%Dw|uZ!4(cdP$fjmlt=f>15>=z8=}eov-{%vB+CUYeTlSB=EDf3?Z8sg*t3_+PC@ zrv49a|5vR2hYa|Cb?pC1iO;x;BegF5U^nSiK_U1S8S$fC$V-T$lObefL<8iZdZ@t0r8 zFycRj2mb!`Eu{3pvZv?zsDgO{B|0{z{x>ccl25Xw%{v-C>+Qv>@;7(l0~m%7`++-U zasV5N+1&n`35tSS96T(4`N3`iNa9%-#xtWJ5B=ZsMMA;E^b$T+r8pkwNAp3K=q#># zwOn@=2xXG-`2q5o-?!WPJEVLD0fS~q0hDhk6^&c~0{6NzF@aD;_N4{yuU!m&40c3J zR&qe`fAgK%2pa4a`)34DY0N4gH((UkQv{qi{@Ol4MX;J+N>mrW4b194llx zIarGsXbidxdQt@F;G0A|g!jOH$dKCw z{JkF+Ai9ev3dDz>gHPsaynnF&MR=r7p#6+lDTY2Nmrj{$FaE`Kq{<_WTluld*NSvc~g;ViyS;VIY{aQ{6zIzddVNu7$S`Y8t>WpOzU3oAS+XY zox}^IFOKB~l6VRzZ(Hr{d@mpdexMkZgp&juLTj(e{jb!6`4a5vJ4}yTDDat_8@KPu zKXCbxJ2PEv+s|CgZC^e z@U$pH^!cO1>(5XPpMx>6$L8A6+u}ufMe$WtmW}RgCUhjfNH)@W+QcC-kNPGnETtXB zi<9Qlm1__*?^C|eSpz%$FLd!wr0}^awwyzs!Y|M}?J0(q0s7lNn>ofy3{yC={B;74 z7p||ESbUVh>|9fcXVp&X1zZ8XL*#ai!*_0>8?sW)2tl@T$kwa*ptTR)tV+?eRZoWQ z9EwExMLHstl(>zna-FB^G&UwH1R{tTk|)Yc)za@d*lzu(Rb4JnOL=^gUZSvkAp`s^ zyL7$Jw9VhQ_E^xm8;@;&FUEm( zleN3{!?aUXc2NVSxyq!qf_sZm0vlwgU%kuy2}ws7EqLH?hg4Q?Vgq9qlv(?4{Ot?~ ziy{vmG9q{8+AC1`Hd&{i-_hjh73pXvwH!{E-u2wjTc2xhzj5cD@;M#lo@xEFqVp|s zj>m5YqDHRYq%FL-+^U}|W^^|R<(#dwiKv>V;xSM^Kgmn6hzH2G`25!{_*?g5jxZ!yX`Nl7@=ThF=GsP9J$=GxbzgfFFTio@I z1d$|yT2w&kZ|8{vfw&SM3qqW7I+~}(<8;4tA{yU12sozdjV`ixx%g-nrD#sf5;mg_ z$d>m?emTA!-kiv+j~vCfu+}Au7iIiD1Xh@gmTmq6jytJRr@vTMNQ1+1ctxM89AJpL z&CAp%qtudlCyHg`HaXS-ofC5#7IXczTA`!ylXdb>+rS&;XLGP zTL9F~Wiq~Q&3u|;XQo}kcfU_l`rK9-(bbF*g&V!q*Z`QK54131#>e4Mb>I_dM5#8M=9Zm#%n`s8&Qk!SLQ#rRO8Wk{O0 zdO9piDxm3}(*$oiECMQlsubtoy))AaHDuf;)gpFS8Ax(mz#zmlB5_lHN<4EwIzue` z`<}>gxp{N(`F1pPmTpkLNJjy8SlGJKn@WOs`VPB#DkGiP3ld&x zMCnzn_a=+#?+gN1Bd4391W=rIIaa7E*H<|GB9%7k138XZ*j=oBhq!I0iBhh?6oI1& zd5U-R+Us#mFV>lDKEKtL{vhW0Mw?P;rBgwg1a4763q5?^)RC+0$#uUjAUD;)PgqIsJR_|)bmIw(^l9Bet z$2^awmn!l;j%8H#fUpW8{eBQHJwe zo8-oEos22+{XGX}r^)i9YeaXH-`wKDDL;8iJY3)V&9gtAEdiI1ve0JVO@0=f+fHRd z3ctC|+6>Dvy~Ite3lKaFWVc51HFz56u{J^WR&P8kP)$-z<~Ijk&C+KXMajo%-t=Ps zhf_rnAI8rnPjU@Jj)c!c_HHcE7r$aV2r>3Br7q7rY8!&(K?bwf7}kJP&?NP~{a>y| z$vo^}ae*@C7Yea-F{Z0o)QUu8MW1h*5Y)gTs1-jx#B=o_V#Jclo?Y^h*b)_|&57_D z%6O=Y@8Mcx(dxH9ZfM!x@O)|cgZHd9Wsnc;M~QB!?6odoOz1Xn{8F>u+Cl51dHUmk zurV+tF(@^{)aD<0FJicN{T^gz;}rIF$){N-PZfWWqNA#UjR5M(vbwa^Nr@6o#^jMV zI&btpsd8i+_LbkgUV7RQF0WY(vVe)o=**)pcbRJHK*f7oVfAnHhevS*jB;O=MM>0N zn>f14Ivt+71bBv_sSy#ubsW;vTY^#1IXrEh-#pon3ls6e_K!j3prMC>=Q^%~fU4k7=?s*}NJnI5L z>PkG4fsus=#P(f-V(B8TJIovL{pB_o_&$JxV<38bn{?`z102VcDop_qbTA}>mE!>L z!1Cdwr6Kf)R2L(tH!~1fAz)JT^y@Ys0h;mCPZ*>N8^?;%tO4de=$7QR$|Thl7?olf zXl&QXeREj7d+xb27CYwtf+#MI&)4{Gk|?k(Iw5p!w2ww}Rq`H%6Wj$LgyL7H(4!n< z_OJ=f?MmWN9J|_oL8%Ou6o9IR2*zL6Bm1~W9TXv#u^2V9So^YrOzu)M+m-F~z3aHp zn0DhA87n9R@We|@1Bc;^hb$bLnR>lcqU>ix_T-Ci<<4m0NZQX)rW6!pYYY?R=B6K% zjyEgXi}BuwVm}8?wBf{x|ckpDh@hZA@3-Z;GVLq0p$ZABV>4wCOA;|u}6Wd#giuBKegEDMD%b09+2BxPSkVsi#y z3d-am;kiVAA};9$pRZ}2(vB6?nvD}9KG%r!)G4be{oHSATG)N-59XbF@a zU7j5fhqsnH4E#6l$BV;C7_y`K)k(Lbq;_`OqbK^jJ{Q(2vpW32G@4GFy+AX|pqG|$ zfjSI1Rb4WVFMYQAa&YhSH&2ooQ87#aCY`Z1#ls|V+AJUy#w(Rd4A6}a)SChtDQ_ye@3ANi;$Wy#*_UF-Z8pZcq!#NS!Q`uOljR?r3N@Fa3IX zlTJ+j-7cuS?7n`K-3RtM2;iG}bc;4*uGY!$wxnN8$6>quNT$$6sXQuVD}_y#9z^=Z{5nzwU!4LlrONOJaSn&kTXZ4G#LKNmvr$4J+=k`6 zJci|fY)6*$ME~8CffFCB5Z-DbOPp9UaozoqaVh^G0ragdfLmj6*J1F>^P{1yxS;&`83N$6YC^+beFAB-EcqFQt*YXcPRR-K{l z^Bo}CME%r4-u!E|GH!TAT8JfDKfZxh`lqc5%WpE@ejyjaVaF}bOQuA@QoG!x0I2hP z5CAOF_6i4cSFkno%lS0F`PlAv=*V^)_tpdnN=$*n%}K+xi5&Xg7l#}ADk=OQiWAC@ zL(&IIBY^H2kMI1mK?Ait!y-*5#$E6@Gk%atU{G4*A`a-~^6vG#lU}@m9I6@0It3bh zY1MYgGb1bUaDiKK@ph_4(-M)@dkm!cTKi1o7fKu0R^7-2J44IDKJ{{ZyK2!mv zB-(qm*$DBN4qvwdY0r&Bg8YbIM=-dP!%V|-N8&(`+iW++inN220*#JFG9KoXo?j)$ zIOO5Ak!JUmH)7k{v#rnpCyeb26ZoOj`ET7rxGH34Nv4__h=bkahlBtj*teJdZBwI*$Y;dCgdXCd2Iq&Z(>_@$wtv zB^><;94R2X7xsk1!wCl$d!p>lPY-qKoQ<6^1;>qUTr}01Fcz8qhRnH$k)2T_WZM82!g&UuAIRLb4C@0e?04eC^8a(4Bv zd`L)~lC3{-Ny)7ZP$9(_0icLVT{TrDF!(L+4l`lLs_Gw{S8Ol-u;w<;WgJnD=j~kW=2V;VF zDI+nk;sPzjnCoD$$)+Nu`R|Slm`CQK>Bn;ZP)AQHxhSP6qAVLisc>}{&sC`Ntj zTA{Dhu<+mO3AT)&NRQ}1ZB6INMcf=vU_kpTY0%r9Vn8zrR@0=NRow987X#MS;n(06 zF&zctQlj~!CM3AdUY2RPTTK^IUxrIuL9Z; z@CV-8W}b=2HOs(fCeKHF9cO*JlW~(}SbwIJ`#ocU>}Xs)F~YQqtX3QG-j*7h6GiE8v!*?AAk&FPs=_3Ns!dY5SfDKt_TUda^Vwe_RF`!#veO5jA5 zalXgknndkFu5C&W@+hyFsp=EVn`76Zb}fMRB`Bv=*cCu0n_rE#ylPb64bsjxnUD}o zu#(rEPhw?o$O{cCGk@q3tDOTvjJbisxv3q8B#bf{ifpum`GvHPJsWPZXs8J7Lb^0XXP+C|CawN6FWB6u@H6@H^;ZWg772&vq2YX^IA2r|X99hV1_)Kp(7)4C-o1QiMwf zo*l;nUhgU=h?(4X`BAfQCmm)%71FGl$oc7q;M(Rxi1gZQ@xi^)m(KHxZFp$em)H6` zLa^;X+!<~0^}sf?G4RIu;$`Va!vzp>m_*QH$PR;a*}d=pE72;-bDWGa)GHh*(46-5 z3t**5KFFXyyIuv0u{bXl#ka5<^f~a0_pWVmpl}?t3_52M)x6W&VczJjz~?3NYg*_K zFn4s}hE0zg(gdOYMx;ibnXf2{L!xLXP%wV`!DJ1-n;)bCOzNDctmhrSY`hLm+g5XB zQGo2cBMEN}3j3Xu51Z_L-H4^uku+|R0(ONMDFb^z1|!5AB1q@dW_}1om2!;~Ma+NB z?gF8Y4UjZ<9mhDY_^LbEAo&jhWV&2G7nty3eN+YXnB{?Tc|+J9vQth!WZmvG$?pqT zt(;dEAff9NYBBkurtEREuQ`B79GbFmmFdkZ6rw0ctL-&wJuzLnAITK!71{Q@p>PcO z_ee|1nJY}>~O@RO!d87fXtQ$sxRF_C!S zSpz}zYh0c$kT28Tv%X3zaLKQEYE)C8a%5Fc)p8s!&Wmmiql&7A9lfy3F4r$HPyx@f z0!}Pm$VTy@&+f-%z_jWJrZs(d(kO5!lMS1c>kpt8H$sBN@qn^Bw#jqzn#(3=SqwUe z%?n_e?|0lWVwn|`_PzB&^ZWyRR%D)60n3X+@Z&Uwv8Z>PL)qcl@P`EZOkYKI%}icE zL>kJT&(mK5OgxYkbWs!r2RY=5v97Y|y?N*k^V~Gx0>B?sSVfu5K)E#Zw}-?u?)6Yv zDuZ;Es0>g+u_IyF&FJu#4H29XUwfIce%yz^ani&*i(4*EH&y$rAQkBMgfY4g#i-glVjoV+uUB+J0k=JYFJYR%!->tinkTASC6 zMoljqZVOrkr*{DYJ-(vtRQ3Foy(&m^N$9h1LaJQro;RS*r2)(q6g=j7py~&)TE`iu zidJ?&LDhgNUh!wMQDSwxQmiWrNrTRl27$xhuizHMY)%AdfXJ0EFV6MX9z2^V%oHz= z7l&NouEDkdbT;K?Z56OFD&A)g*=fJM>FEQ+vPzLoUM?UKHqvrHg?YKkq=tTsk=>d< zya?M};&WmNZ+&e2g_07>cXWlQ-!sNcls10TXr1{_PfNWxsH>NUXwskGg%dx*$i~h# zRfCPnbrh&1DDqphP`d>AxA~teUsdjuDoRnU1%6r;;t=9Nw(gT}IZ8>ekgpNAI%+_s zPQLFnQHYX7biKjj ze&+|T}GP%oemelem&EDKsNEnXb|95hMWMH)FZ2T zJIsj}rHISDUWU8=$8KyJbm3sTXBromLP_>uZg4q(-=(xd@t76VV8dPRuc!#{pn!9S z@^zA36=-&cBWc0~9YjAKcAEQszR|6E-xW@Y+ajouE~28E>`SdBpvOhF>QHEO45gdl zX%Mh_=be-+U|CL?9e8=}eJHfe;|%T0P6pIL0UCcrtM~W%_m#dlmf}png5%>zhyEHb zH=mw+20LTHT4)hCyom+M1fB8&g<@Fm0oIXNDaz>Vl@cAm>x?@YGx$*6x4ltRBW18& z4+0C>X#9~oK4W}FFc}Nq6$(!mRvrRqLm>G9SBS z(l@;Pe6EFz>U*wMADy=_9@5L#b8gL|u+<|Irg_1S|9IdM;1fW2-Ne^q@^Y zXgx1wEJd!?aXkAzPO{AhB-krmqsQZuxZ%SII*Zc;CN@%t#b+#*K zhu_@>%>|qw*O8?WUdEN(#F?62jnJ#d+TR6^@#SwX@YU&9-?9%luSGj|&d0CzOGbl%%BVAw-)v zyUDgc-cbm{&TV7~eFD&>cGPu~hv0a2jCgrFrip~aPk*oHR9VkSP|~;HL!Hx{5U@|y z`M7Ph#jqn+ugVuNd{(MBO9h;E$vjTk3cE#mv=uSY=1~Mjn503Xh%@3NN^*e^OTY(^ zqX`COCPf0*kUb#iWuL~6hTSQD9U;c6969WNu&QAv8`^UVU1XKlLr}Mhc_sKI7AKdo zx1aC7cQ~5N5%D<*_Qc3O9TJ(-BlRHdBNVuarx($Pq5Uq&{|j5g#iQR{3Ul8m=5!@f z)etrnbl%=*luM88?tJG!4?Q}bDU{L3&7Z0i--ii)0c~N0Fkw#Mxx(q`c~Xx+rK=;L zMj}Om_6h0CPDx9s%MuwBHeP;T79&aa=-Vnd+E4Q&kxnv<9Rm?tYz<0Q8eNZgcux=| zZ>W5~IL&yZUXomc#X@8|!2Q3UhoQ+Q{0)>|85*X%>t zIBm1@w6ga|R3Z-M6S5P?d%ARsMq($H6M6nVf_IAs(`e3yKR~j4bBu*B;O$T<7y-ew zFZxRIl^5l6ELXFnKt|fhG)AI@TT&(Jh0mXz875hpp$|#@0oJ+t#wHpahOg zqBhtT(Ur+-eAgyiE|tcBfR^hzA!Zn6DXtM`=r2Vru`l?v1>scqMl{dOsO0f^UmiuFz!3{hId?YqzaGN-*#HI-HX?azTONz}N#CKFC*UPJ(w#6NWS6 zO=@>1^sKCYR%iP0%umf-w@&6MAexwO=k;2{L4j;$vIP%g$rhj)M(YRwRSHDFwU@BT z8!RNn4ODdGk3V!A(QcdbN;tvkcMOIr9cof?rT_N|ZFmDrn7SUt=R)-)3VJ}D15c=4iXAL`5kQ+-M=n(mEBiTPZ;ELqTysph#JJLS(x zj6ycca7l@I5^)(dL7k5G#Pr_2`^(z1nNOub`}P&ken;+RS9Zkxt9B5yuv{M|`@}N; z7|~^Sr=-bbpB4F>HMKb)ExnlvM024b^s+}WMzZw!n`Mz-7u6DTuc63Xk0tifwQS^)0 zjFEgmb4}|S!B***GN~~A>hum)3~Tp5N1ruZDsh=17wDPu`&hw3Y$_xTKGrU)0-cC3 z^c{$Sy=;JF)Z8NWOuEvUi3!+kT9BFIW>|m%+ZK*FKayuOJIb5Y{sfy;8}O6@sxbvU z`g3cZ3?Z}^<3;r3E71t8-UkoO$nkgVe`E$}Zgo#Q^ttvFdWOx1eD1RK0>haLP6_PR zRX8H+dVKJcG7;WiWxPm;0&i+%?3JSHMLWl@63>p$>M=p_2n&oepcGj+6TC90<)vqK zDj5hn4|aEe`lhz@`CX1l%(xw&=nb&-vUyf_KM!smThLZF$x5)U`iRp-J~)fO=z2yAPm{~gM)>@VHIrOMt0-9DM*RY=R*;d zh{T^aqXxm~Nv`}lncs84tbV+waC&Gl2}XtQ!78^~LUj+l_Fe=LO+HF#A?ik)Iq5r! z&J0GRHf3)!=*^L}-o*_Ku#NosM)E0;e6D4N_EQTqujRw)(KoCvhm@~&-h#5`k2hr5 z>`Js;S`p^hz_kI5Sf-0!UznnP3WRHUl$?gQ3DA0C)k8rBwlQ);aYj?UB$P4;BbBA)x9p2LFnB&g zH*DV9{`@UIbC&g`B=Dq4?R=QXe?K8j9Jr_D1|7v!5St-{@-foQ%rNEG20!05>Ym$` z3QI5`Q5OYrdl`HY^KrnpQP4p&_Pk3}5_?hs>h@dk@dI~?cXO5Yh{wUW7#S|a-u@Z< zOR9)28}e&38Np$N=RYKr%x8xyd&OBFlXZx*Nyw1e0(I?bl}zkb`jE?^Mb}&|LihrSvF0gV)q%4LQZPIlXg5ftx9g6_I;Ya^?iFu6+wA{Ea z_mQ%Vf@Xo8=N%nnb`X6f5-CGCJw?X{2GZY+%PaWG%Qb#Pi*G z*~5Y-L+M3z%-V>EOL}X<8zjJ)rO!7Viq5PlR1)~#IZMDgF-V`>CkHx!@Vn1Tby|XH z9%f&Da1%hQqi;;x*<&p<2^`)n=&=ysnmn0*6Qjv!QdvMZfnMs3DPeJpy2X{Pn`TI-m1=N-#$SOvW)Yh}>vx2El4LnDliM;LYX zj~ZNO8nm~zyValbq8RFqeVG8~4>Ox7m;%HlxWoqObbX_#Rym z11<|={}o>4)H{md^werAJbZ@y=5a~l?FnzwgkPod3Q*wz;WOkT^|2Ue$s8FQxc^E` z#x2vt*Pzn%;!hy0{|Q>BReUK?7vt6~HR3!?d+A@a!B`yaQis7WRx3NmrB{Nt?b61( z{cqhdxvFIYVy^|?|5G=HhxTVISx*>*Uwv!3ry|V!2_s24TE}$`G#|u+0g{-+wb5Is z&fLK!?8hAg@W91iukSuPE*P!RAYT_MRO*y?yOekShv(C=%%fH z^InD7d(=7oVr7s1ID6nN6u%1O;7CXEMy({uZ~xj|V)H$0mG1%OmxM!hK1s?fSF1d- zoXnLkCnFQFFq;bUv&96}RNYl8Ou@417GlpD0l)h;ZYTr%E&J6BA{N=vtc%o(WCG$bs zdyW$VNqgpfX99eA(CL}qN7t-XN4tnAEvlIcmr7jr7AxamSj|4*@IKdcUttfZu z;0>ej9{ey}gh!@{zk4NAiu@{j)^|RjLPdvK$URW5sI?HVS&cHodeIXzQ%UGJSjLZB z8%pJno2B8tzd4==v6&<0Rx@~v<;CQ7xYnKsvuDPX#DQO>h{Lxx4-iQ*4clt1E!b?x z=h1$EHO&JxIlsU=;SpfcOaK&po4zM^0^B=$Uo-ulN(`k3+b`{z*xFew?6il>_`lNB zNvDJ!{E)^nph895q)isF2#~9?8)3H$YM+rd;ss4TAMpe^Z*zo_Vl5Di0`}N8F?I6U zALCe|<%C!k>v8obawP!CqIV^a=H63AP*^5`E{9E*^iTevIuw_!Fprw>lN&3`N`udy z+8}j_!2&V!v{vEn=n0o8)OQ1wWac!@45LV#KOG0`!`E2;v%Gh2$qs(J`?I_Z7v?L? zb3VJ)Vkfs!TZ#ZmLsS=6{zDcaQZ%K9Le8*c1qo?ea32YZ36p~%gdH5tRDXN>-uMMF zl8gec3>Rdz#h>s|Hv)%CHAcQ5iyLMR4T7rGOh8#|yGFQ>E~3Bunw+mgF28lefT@?X z{l_uhx#p8~elWqSsfO#ox-;k%@xreB?9Oer)moYmx1T9~4^6cY1%jNvLN@Xv8D+-W znfHUGldV1;QSPS3z@-?e%M4%Bg`Im38mJyU_?h=2eDp2^<+Dk|47H`f0=>tvo0H{( zhfX5&=bzgv1`pdA&JD68FB+e3HO;gIC|wPM9_`E}K#v17PqBSqDKF{Dzmm1#{8Bo z*%#+)E7CcHUP_-Xjyz>=3j;z?_}wetX)=CHS%~ZG!_MUJtUsqES^p6h(L=v|bo`BG z7azS@5Q+DKHi$V3Gb}&`w0Fn|npBX9<3Zn=Oogs2FP=p0Z`HZ7dOYAyHF0HspQD5~ zdTdlGO@>r$kUne7etoQ#va%Yq3;Uk3C{1cf#-_@OPtNgTPy7?BdWJIil@p(wEnfb0 z&Sf-%bY}M(I#V$D%vNbFc?zbcx}zyft&7@w;t`cLC3D9?H9^h&x>xgpZUoSPCHo^Y zU>~U%|Lw)4!x0wSK!*_{s@ z-t|9=&?$R%zbnHi7)NG)G+fPn^|$du=;_-I^5ri@)f@Hfs|#d1d~6!YdO$R$TjChX zX=hVOmnUOa<$O)X;dd;q&HNgSYS%l;oj%0XRer8?*7L`ySpqj;*s`pDf_MrqFY-Q) zAMS-KbG*KQ$ZGXMFH3?)>7d7%-v~tgmaDvp z8MHcaQV ztf0m%>Tc+&a~iAe#1}{Lh|~UbmGj3(dQE-TtYL?@)USn1NkAKLaMp^3IOv;q;{KRc z7nw4o;wXK4A27XBYuqpJpk>cAv9b)HSxO!0U`V0tVR_)u06%EHL{L>x8KNVOF55J> zIG$8p4R>uV|FE1i^P)ds{?1al%{e1%Kg7E(oL_%kkghe?Y83Oe zarS?F-JL8x82TU6cuf@EDiWeu(mLC3^U@zor&HTnHa(?+5I+DD+sWk8A7TaC(s*#+ z+}g#Z8cfaTur$2%CC(nqI+JXZ?BA}oz2{3j^i6_* z-Q%rXZVmIF%&GGPZ~FGlKet5P*MeUF4b@Lz{efkRPn+u}VXI2-wzg_qu;O;5Q89`? zvsTzY$i?x|f#&GJMFC2+ichV%-%N?H2jY^#BRa}mnzQkJkuSX;KVRz6$CAv>GRgFSF@?0AlsXjXHshKhb^5<6~Is>?STx76aVEDL$t|>0UrP z^Ro2(^9TG9r$Il6NK0Rn%=>|c!5d@T<5MaKfz(==YlqalQ)_v-h^*W)K*-XVq+*QSDv}WL>RZ^P4bu`{tMS zfk^21Pp;&+g%VPhou33+lK9KFD>(pyM>T4wc5uU`G@Du=O#aL81(?|+Kb55Cej?eP zv@=>3g+!2dl}T+18|T_i#mBe11+uMHOAV4k9tnwXraqFL5}U-kf;zRswAPwfnt!T6 zFt)PEX2;9GhbjlfI?a2fFD^O&yIl~0(k$pKL(LL|K`+;7zGiP^oTT_s9~gPeu~SLo z&bt-(2}X9nHkg=r$c3GD>R%+5-9kJ?Kw}t)KN% z3vBa)*Ap0Vzv=iUr*svC23mOMTi*z||i@hk6)>pYgIqz;J!UXh-6KB<$hN!Zaw$GjwWDD&B_~r%=W@=iNLyNGn8c|jRz|RSoEQ7K1GZ1yuNpNaw7ZM(zFGysAs?i7g$tD z=2=NHz}b)-8tJTEuZR+^`jPF)!q1n!#Vq?eE_aeBYokZi1p1*ntsLXU`h<L0u*;PcOIFZAzrRsZp$a6cbIKpHpIbmLW~kF_!*jV}!;0;rLnJd& z8uYWeK5Y%01#)49?~EPneUB6^`~oZ@rAW6x6-cUVS6yCmLZF2U^AczW1T9#SAT4JV zh!Up14%QDlPh8Ml*=fHt5U86Ax@U|C` z!E}iV;GoB`7p{6*yOzqG&yM$!2A2Tw!~xnftY-M{i{0RtaDYMlyr&Dm5FwFG_x8TO5P zPjA3(A6`+WMK?}~$y+AKb7Mb(ceH{+ECkEo# z)LNt%o{VIc;T*WOpXZmytjYC8)9L>NN`N+HHRygD1j8{**w=2v0->}|i6MX$NF=(8 zet=uh-U_ZQ0Me@}Cq(W(vc%H z*aHr4=24qn|6X7R9(eyVjcZ|#Z%~-z#-psSO8%~L{#}F^J6OuQk2(l}`io|Rz{7M( z4ETD;edDe=Rytep>l!%rXhJ(d!#z-x+!wp?L*_p>Uz?=?U}y`)b+f!-5^^pwa4pID zJ!z)wl`airb6tc|-w|7Q!0vT|C=s80hY|X%H=tIuAK^)Tdu2Rdw5x4Fi|R&CJ7@1v!ujZXBlS#;pLXPw(1+c;iDB?FK zE52J-*hsPb`q`v3aWWT#mMoTEunq@ESVspmUG!)2G)JeW_VD7IQJ_dZr`@&FDfvZG z3g&6Fj`+-eF*y!rO7e_I9K7}W_E}B1Tat2>@wd45X9sB^T`=%9KIj8JVeKD@zAdoo z!zEw@`)HDJ@FGU;7&IZMT@7S`Kt_p|OhCcSSy_(HjchclfA+!TR=0;eu=0)T=rc&G z$BUsJ=8iOBd)sf>3PL}wF|~oV)I<19*N;(D0!>JUAEmF!3~$dTt3en(sOJvp`b!hh zOBns6GQue~Z?3!N@%1N)gEEW} z9hzm}anw&4#v@_tl&IbN9DoBMRLh^x%9`}fH~YyepO}u#u2=u3x(&cDr~ucP3zIrt5}Rc!+NI|_qi-m65}2! zkgp?k>Qo2&g+I#Ul2N*mN0C^V6iumhhb)7P#tH4f-|;z$J1{y5`jFdDk=IZzRethq zKi-~@q}|G5X;X!b9CjCZ_yoN*{qLszl&U+R{7-(m1B@xn&g>8SAS} zJXuBl&9Z%o)d3pU^`e?6%UjBscMYg(Tv_g!QdaB1Jr@BsmS_j{W0(E)9x8NW$P3q z3f`!(P^4~eYnXV-zSWV4g2y&+)u{MT-}Rmgw(PFlg#U6{aDYumV+dmV`ZqB&ks*|K zWu;(-3A{qaJw4~5lAa+TCRyJ|ltLRwn8IMZd%T!3j(}q-)pn}GG&Ek z0@?P!s#P}pjE;yg)eURp|hCivL(|unn(VBlG9%udPk~Bz@UpJR9FgBc%}d z&XCDgDf&@qGdzBAk1eGx=^&n4Ir7yK%zc$a6y8;P)cu$$m3iy^m=97{C>jw^+nS?m zFF!N7tLP{KKVV&D%K`6Y`#}#!Tl&2}pvYs5SzKh=f(&%s`Dc6GAbJAhfCokL*UKjbX{(JlF&nsvT@@X2kwR;_5 z4~d*4EzfW3u?|oC>g`b9bqT$M@(5e$u~$FWXqrXe&xfN|aKWux`{>?EY94i^MDm-% zt$#(}LO=(S;?6gpC zaoHMgZ7`O`9?~bg_GpNx>KhP1Tt54`rZCBbonHj0P^Ls4lSrUFC^usTM!)?_6iU;U zm5&6f{)TqHQ{|e_qU<`xmKcxNVQx%+xVA&UAIkp}9reL90em%vO0GkO z#h6k`X&dqPWm^``O!domLORy2UFU>t=||>~O8kTLEmd6~Fz`%?ld3k?mpZPymkbqc zV!nRyK9ckcAelhr@2R@B0)7>8#Hcb^pe6ynfeMdMa(`UnzFj3EsBD zn>GgQkFWmpg=0`mS20ABp^nM~`Y}7npsqb*-}*)153>rRLq#d*{DRFbv9RY^z+g-V z7IVbH!#q%Z>nG>No>Z=`?6->EZf8;?Dt+XUAjydwQA9I9p zsJk54;`$@7qUvq-!-pqwXAk>NunCc{agF{n4iL5yu(S7)S~Wq4R`ecNf_np4L^$C z{yV*N5-tCl0Y|E}3_^>%s7@iC*7CnzVMXKL_m_*nmbGHMmW^aIrjL|HoUC3Jnmq5s zGz{j!wUX;H_mVsqrg>^q(A^a;t33Q?mUS%qCz>AShz8FSs|CUvlGE*4S+j2Q$L~aR z1X5%!R?yCFB2e6k7l5-Q?n<;`)ITg53;UM9Ga0EQaohDiDC zR9(ip>ij-&aXLSTjHFG@Z8JhVvhA;9YMO=xkGn&iSsv*BO7nNKABFMi%eNN)n{3XP+J)<2o}u@rwnF&=u7pl8=^nbicH=6$nL-nAv5C2nZ572sEH-~$; z6d0``-o3abAgq7yr81p_KI8t!sTPxGCH^I7^O4E}oR5&)sDPdEoiT zP^H>v0-7)T9#!MEcaqwR9n*A1$YXN>kNZbn*tEv{o4zHh1r!3@BQKg<@9RLKlZF(C zVZ$p_C0x}He6Gd+xJ#}hddEq0J_fBf*iYnY_48605|s9#1K(H)Vy!!q1o_XPRP(j7 zy9k4#zk9Gj2`YEj!eqHdco+1^COc%8~2AL z10C6qwu{_B4tVuNBC|b-ZNE5UG{%~r)gIT6+K$KIq_yPa%xx^j-h&oql=VYv_`fGj zAxQ)!14oP`U$s2-vAWj=Q@*sk3E-fy2e%U+xaS|bs{*U-Q=bMsx;%NG0 ztaw~;P}ieV(&LI`{AgHbNb~L3QRPv>U{=-u377O{`_hBn?=$Jv>(8pE&)z9BbgrJp8{)AZO? zI*J}tyVn)%rqZ+bi($@o$34L&FSq$j`>Yssxrf$z>@v#6=WY1$X?~MF7n#Ez5W>!Dan40YRBUT`%%v0Eoz|XGN`)R$f-yY1`xguW=V$7`gRo)WccRE+!Pz7WwU~+(9;=6G~}M$#ysz&#hWVqtuI$ zJ}Rs0z@Ue$Sqsmg;@FU&KVbKi$N03v{Fw)}uIm)7+SenTKw8C%6@Ki;?86}QEyXGr z+bPz-b5yy--Kt%tmq>uL&gy;D%1ExTMm{1eiVD3uQZ$+kKDGqPW6h7{2cy8hcOx?b zgepz3PA+07vuC-!6o{f_dS-)OExoS#bx2`7OO9uo_{*L2GF3j;XAN0wuDS24%U#lO zupPtx)YUp`jk1Vz+M(A4wX9-;alRrcJX>t#-KE`ue_ld|4c? zZ$+m=`F@I`esH?t*6zM6-N{J_>T53^SbE~3+w@p{4Y%#0z=Q|&iD019^psP4gm3X#x%xorZWO7vjIK%bL4=G_D(3}gHM+dZrW!iy~d8gE-CGz`BRR}w!j zH&=?ga`2XRHe!J{5Tc^}BuE96(y_;uSlrF8#ukDsosEq!gCse4sKj)-!}BSH1s*(I z$bNf-t^w=5!W-a2q(L-b&V=V~u@K92kmCJ*Q|;7rCtgtEe3GmW`w$q~H65vQ94kMaEj>a3F=i}yM> z_pWi7(4RN?3?xWyb5ozpZY;uqr{d6ea?Ds>{6ST2LJ8Afl^%zjFZ$W{#F9|#vo`+) z*%~gYWXFr){lAiD_UIVO1H!?N_9nG#TvG=nsm6ru#%H&jaDh zPzk?pb27=;SIOTtm*)1b#iLsFY*zp1Sku!Tv^a(0z|3KOu8UfRyq2sAefag6C07?Z z?8Tl*ywzFgm6X?k6zq5`YR2sM#jzufAem#+D|myS#^Mp!AW0qKD>V zsmWp=+a2-7GEOVt7_p&(#XccE!8c^0Jf$41JOB$>8(c;%zuZtCuhnc>h#HeUzi|XMTh3C9< z9bnhFi&4td8Q}C~)yh_0wPWzSL;}B)9FaVuNSk_}ECU6$7c3X|IHLSMy3e3Y%aPVu z*_Tk|&c-xBUvCvn4f0??9$N}M({6s&DOdsTct*Y+&qJJ20N7Z@rf=M|yDYhYttdKC ze-<-XNY=vr`ey^vbsqW~5;5P6MxpXqMb~}D`5%>mAZ+!sUm(36yAF7P@?Rv%=GA5m zrR4>9z5f!CS62FnP=rkpeK6QES|7ID7G&CXcMEw4KfBzbZ$n~8XLn%gP9(Pv!^ z65il_1=+BMBhAw?XJ1N$)%ja+_ZurDIRI2j&S}v@pI6G;yu0^ey#9sgRNKg=cB=kg zjhr8qF2;7Gu%DhE>Ryj3Hwkrl$$$*x&%Y|0x{ut17Xkrn;}g%Bn85Zazl>2|Y_5PO zG}rSvtkjA)wir47;x59vx{vP4$RYsDH}9BLFwr21&p6?sLnwv+>jh8<7bmCmtecat z;O7BZLTPdZy0Dp#Rg}~V-m!xP;+9K~N6mBVyMvr7-`p+)a9UnGoGO>@zWV)v_mT0} z2zb(KY+0ezVhrPh>aAd)FynuA$Q{GcRv=FO3d9>H9Vwo(BDNI4%Fi>{9#BjNzAD30 z;@BSKc#g#r@U4wCdfNJwgAiPdUB^AkO#C^e=a0DHC~_N@URyDDQTA zAMlD$deDg^CthtAr92<0DhvMozOdB0-7hBpbkc?_ZBY-M>89-sZLbv$5)jDLKxMrI z49{`Evij)W2?FqwLp#Gx^j`O2&hmhVjl;UIggmjg@=lrMywrmBsZ%l)xU^?Yvk;iG+XGBlt$$|TashhE3AvF-jzF|IfWiP&Z30=79{IjcXxbkc#wuZHtDfhtFg zo-Z$G@DEZnDz1}6Y8(8Nv}!I0r}us->$?Tj`5Kp5!6wi3XmY@+(J#~H1sayGL>-7w z9tXexd`{V6IviSoU?c-0@KrvYwHsyBP4@#1=(;en?Gn$0L3sm0bmh!rUR%Sw_c=6B=Zr)i z6L=6P8Nx?e0q+3_Fv`JKJM2Cgt5;`xnFFm^>P8&AmCt^p$t&SSe^Qfc#@u>6I$E=a zLRK{qM9l-dK^ezX3V@rA2w(v_<*gL1vEgY*U@tVbtiAC4nBdzz-%S4ZGp8kPwV45O zijSz@6U)s;fQM~`4$sK!x?Oc_cw1C|4_5EhzEkWs;-fz{usWR^8vmvTnMwh^sO;P` zC*{BbSlDk?l-0U7OQa@Efthn5Bjio!RVi)yNk%1xH*+NX+xaSCr$Sy^C5xtRzmS5) zUBTOsTelIGQKv^2OGbu@Ojv7a$63~=dab!SOECD^Z48w zEM_Q8|5VMS;MsYw|1hMuRD+W5kEwfIT0zP!&tVJlN8R2upMGQue=q&S;v=>f6f>S> z4~OM}#9}u13c_fKq@HeF1)1N?OTEl;wxqBy%J(c@K(xI z!s)V*p{#(^S6FALM2;Zp^SGf_-p_t1LAvOzm{DCz6%)*Z`=6>p#89fd zvN?4PNmrcapP^9&(4WBKLcR>pi-olfBA$p6|6`S<_GA$e=M& zrHBwci_R1D3Zd@+7%5|pHrEMG8}Mp(5Q*8wiH$KZ*Ul4T1oVotCU>pWeAOo4J+@%tu=0o&zQAR zva-AG@0w@(&r5nzIq=o?zPA1(q6d?q!`C7wq7zo#Z8MosEGquad5=05asQDC9HIT! zFHTuZPVBC51u226%o3fp^2<%@ZWAyPNB0U=x=0v>gxlqK3? zGq}EhZSU{<6sPG;zJ%W!QDGo$je{iNeH_^*+jCeo0bs}n4M%5R(o@{u{EyexH3(gB z^Qo;8=j!XZS}Zf(O?G2*QEW7vBSR)0tsXk>_(f#9p~dODn~QLKNkXXZ&>@8a~1|1S!E468A?Y^5v5$ow%+$3Mvq1)o`%YJ% z!8uO2g4RO&UK26GUXdli1if`y0>pQFXKPA0;YCFIqZWq)*%R#<-)?@Y4>Xu6_B6Je zoN0+yZG8lt4J>zskKr#VNFLiTt1-x9-(Sl77Pk&aCzpS|6@1(8=#8HiV|Wb|K>hua-IE?No!_~OU+-z+rS4B@R8b8XKK6b2a+Yog z;ZF2-yt4Wm0Hm;7al6c~U{bMc^pJbzcfbQYqf<#vr<+4RPoFsy1M0oGF|Ix=21A1X z5eKNWjHy8Z-~LOxnF6%`D5GMrmQDA4gyG1w37(xtN++jN%12g5y!1Q-J$k>BFHEtPwL zYb*@MGtmmTA~sI`4zzs~RHDX(7~rGPtOA2H^8JST1|MgL<&7y01Uv9)*@5fNfqP@; z;W86&PhS{@39F1cwL-iwddV0PSas z>UR3dbhaiqAu&hrCD-$>#2X|>tyjSIs}^U*3apZ%_LrOXCz{C)f!`DF5tnbsp4hyY z;wqW45Ve?pRd)rxfQJ1^MEN@S*z5+l-ba=4{uFrKdZ%h&A zC`i6|E=?n>_p)t5{wHGJsY?$6E9Ry7q51*Sl~v_{w#Kg8X5vRW0rLZH5)X$Ll_uXy z8I-Zo*4rN=wZFDzzLK{>uDMyf0MK!8YUX`ipvldj^;Wk5AVeMN?e zxLx5L@TaQ;+(RR3>@^y?=nAxQ1b^A1F&uyj4!49aTsJ`yXyh2c>ku6;91{=7ubSgv z-;;(&S~l!kT!l&0mJ(eBb9k2y3f!PZnp{?!vw$p%!&ffF>-obU+4y%3hngMVa_6Lj zKg7h_aiqO1!B>+1B&3R_cB-hZRw51(434R34f-6fQ`s_5$6W^Q&dscU@~Ne9bcRyz463zmm%k+ve^jf?xYkfAPRm7C{j_z5E&N7o?tM5 z1Ob56q&>Lxo}Sf9aFgdf{0>NV>Sfyb8%NzlBbWoZ!p?R8_F3b(u6zkXx_|2?wA1gu zG_d*AlHeBq1q-=8?h|~ot!q+k6BP*T3Tgb7(wv|a|L9r6k+{wn&ms zD*!j?m+P}Ooi9OZ57fkd0Z2l2r7K`DX-e$H7m!t#t0`jLoPNftNk5+vC+7mlCDMHzPgXfG71?QaQ<_kDv@A(#(~-eyap$OaeU2RFE^FPH=UL zJ0;E6PK~_*^eJcTGvI&XC3qzCge3r+)d0hn^m6&`mc3!o_k(7x5T|qhHBH5DB@_#X ze4nfqa65t_w}C~(q5r^F^K0`;wTCYI-1%EmF9$|!6hDA>l&d}gN3l!b<4RPznKr%S z0)iI&)mVEB&7QhA+>lDx@oOp8L*h;=k?rX{LRu!tRt0yhQr)J7Aw9>D)TaF zS1)MZ3;MYqERZ)I?}UtM32u=SqIvsw|JFycEfd1){~LYHrlhrLnv z-H^|Ob8#M#x?BJKoq$fV_y%El-S-SwUz40*U5-&tFu>+U8v|O%1Y4X1FsuwMi<1QY zc5NYLv01J(6{Qw+%hQBPiqqtn}6w^O(`s6#FVjAhM>N>IY4{#rj&%_x2vTV-Cc2FTS#5q0qo@g?WBZ_ zOnKG!PnSX4hI;5cs1_uUSnG;3J|8%U71^sEfwNIDfU?Y5F}zzCQ=!j4qvqScccDof zwiwU_9(5`(L`=HR)v4WcWK=`M5~QZzm~H3dqByAhOZsrfoFd5tc3BkU`0V*_NDaN$ zU!@%P_urdAf$2u{a0HYhX`&P)d!WI(@D-oXy<1=bvj_esWxc{4>1j)4)EVw-9h4T@ zT&`*&qTxifr*;>iSjA&nNgHir5v>no!YsB%p!_!w{M7p*(Qg%^pcS^!dJ!!-bUu!Y z?`%>SG2ayo(Iw?pZlp}5c4kJjA$9wV+*uVF?^2R7=FYyKo=$&T)`)A}OD*+@u^>TW z(fM~wni3_kj^GTwDYumYV8C>a1F-aGfT3mzW)pT`oC$`N8{oPful*8qUy@w^LC=*D z2LYx#Flm6KpLN`g0+E#6+V(Ly)zeMg6lR-kYP76Co~y=vTPeSnquwvG7h;3lWQfVB zIe2xcjMaXy`?%Q-KXRy}#5{Y(idd>h1n<1Dt#!xswcge%&Hy>JoOUj+-pk`6Hr1`A z{1%fu+PC?F*71oy4;c-bp^p$;IZ2^l=ClamchI?J`4&(6QD1933}(b_s)Y!=tmj#* zQjCY13r%G)%=>4;%Kn{PO8bA0ZCxhAN#mCGrU!-0YX9}H=seF}YOnqJnMYp9K8v`= zyVFC`?1O+@1=hlm z1J8lJUrTh+Zs9^LZ;PB2(NuUaelEw3aKR^4=*h)pn59vT3%Ve<0?)Dkmr_42pTze& z_o9tIAK2%k8g&qRW0t{Jt12_=_TJt@b__&BLB>!jQInqwQzp+4a4KKY@|E13@pZ=a zW;M*}Ei2jRm;FOv7Cd!ofB10=6d} zOY$MOu*h*V%NAp`HVdOE|FAegPh! zwB4RtWb5+43(Dtdf$STZGtz-W{9;ngDWs5*k6HuCnE!RF-;6T+^gb^BAo z;Hnh>2S-cF6S8Z*C)Swpw3KihWl(Tp{FZSV7HEL$2;9ab$PL{GCNZP!|Hd`gc2cAm zW|di}yQWg8v}%Y70Ii8ljpzbHNmVrx)uuBX;{+^DuMA4X;-s)-r;{4h(QR}-v6^wb z1AzHX;-K5qF){X%SX1xiX20(M!MR}Pwn_9YkAow&fXBu+R{%ziMvn$)e|4-`MirrYFlCODxQ+7qahG2Id zPS(^`m{%Jr>jbJgO;y_W+Th%rdPC3OxGcdH_k~H&84x`LY5No-j0-z-5V$~*eauJw zr5H54xq!5Z)X$rsjezLP(c@~bmhB7CoY#yEj{9m)ybqeT9<3n??ylLLV;cua$odi* zfCWe%=P~xzhrbzL2CE7bT7Zjwb*$rT!OXPr)HgQ@gbzbvHi2t&otq%QWLc!XffI@9emh`5Q-*a8diBMNr}zadN6 zM@6f}qui?SGiWR14n1V|dCcPqJo+@1s6eh(OhlTg#IMbV>s|ZihfJb2huPtRQ&Qq) zmhVG-w^^&%@g&aR`6EfXRQ}ToJ~QTQw!utbzn~-kETa&3y~XU*Cv&!La2~|OMi4C- zr8*2#6n53px!aR2GOor-Ej_0~@sBx?CVD7|73{h<1JvZ3$u>m1!sMdn9@^72bxSTPFSW4(D1IftDE9@{fsj$KjO&jhnXBswYlcYpLRQ>B_E=M@q zU&>;TWj9RBhNQQIsEf#CGa&Ay<8`$kDda$felX568kgUmhi5hNSiwq#*h76axa^9R;^j|WHb8PT;Lh;9;fI`CEzheB9ZJJxz%LIY3! z^zKH6qn4oIQjm+Y2%^$JtZd9CNt9=0KQR-E4>zip{eqsoTFj$>u!GehMjo38lLkus zOs9$s>Gjz|OmVieaHuyp+9KlQ?|a+iKL`Qn<2*FL#e|-T+uAK!_y@gS3H~}pxIJST zoQb>jLa4ncyP<^#LtC12t~9JwJ1)p7n=KXXG#*wY+tVEHM|LQ=l+!}y|3j81K>3)j z<0*j>Pk*;$KuwHoFRHO_>olaNu~K?ZN;o`? zc3iCDB-M&+{hKjR9%*Zz*$7{i5!w=vnB<@c;Rp}CNw|?(y!sQ~gje%~$EYyR%%U*w zdGn84TF64RSJ1C#Uc0Z$`jxUR>?+Q*S93iL`q&~vw3G4;AzMDg*JsYt1EP>NfpxQb zmnp_Y|G#Q0vv&3Ck;L1Jo7zP|o0l=NV8mmsr_!o=wJKtY?YS7r2^7UU?MN2RSZW*}AZpBw zd2s{{Ww1S4(M64Jv6NTiI+qn#>3vxvvXG9P?Iv*^y5{if3B$c7AL=dr+npMK(+~!; zP}W%BHjzMfQRWr+51$jNwHV~9y>Bs-nZ{bEo-?&hIlU~^A)IE_RtoU#trh%y-l|{N zpDD{Q$#1#D?)QiRDdTf80dvY9gyYB3!MxjFW!u_TjIQErp$^PghiqT^GvFq25G z>Y{&q-?X;0} zcKHN0`%{ik7;&LmnAnPJ8{-VE8N*zvmzid@&?pv`5n-;O%MKQ)^sE9uO*(xqSXGDQ za->I3s+&fASG{5-iVeghJ)iVmRRMUM+mTUT70Uw8L|p5owdI7WH0-LA(OBQ%s=+b@ z&)Ix!P5S)oo#DPITMMSR53y5y%H}Jl$`s6!e?Lp*{zmuKB$bFh*9|1CP5wdCsr0Ce zK%l;>HghDvP!7&`X$ToUP2Zw+JMU?gvkS=iw5q&~^@~mIUzPHy3tMm=Olj|+uf8SL zn+f4hBg+;L{fVXH(Xe-}gBOo|+?t8WW-o++wmJqShOK!WrCTD}K@5w4P#IyRUtuf| zAsmmI!Ii4Hi(z}#`7@;t<&gQv=Oq=q5huvQmiLcsN8S;|%17I%W0JRF&Cjs-Vr8&| zKXjya^VwFF#Cb6z{G(+LNHUgN@MgV1!sr>xJ1a@&bW<7xtv4=M8Ru!}+o7hX9l;^C z`yVzI8pPNzZ>D>Y5PqX9I0p{)4)RcLOU7Fz&JFHJxk}SP?dBf1NYFG9rWL=qH8)-Tu~k zS?q+Y$J;v5{{8i?0wFGJtQ?6jo? zUMTRX({-sxL?Ta562Xp8M8uHhFZZ_t?NwG8e^PwQU9HgcMFc;u)va_otNI)l(&=OsUTm4((VU zjtfV_;4e@(_+uGrUl4puitQ7%0ZnxhogOEJwBY2?VHukp33Wt0&Z}Dam{-*$k*Ywx z?vUioj3^X9{EM&&zg)GU=4y10PN=sd`ee*FgT@-$g%cxBPNHp1SP@qLzXT5qUJtHRy)yDf%FX_)nOQ)h*U!IHDku->*8;M}kWWB=HuIQoD zz>Fjg%PXW+G8lPEW|Js5nKOww!a}%`YAkeo&Bzj`@Jv3$SZ$3F@o&8jceG$0JmomN zXLgUjVsshn5w5tdXyX6&Gv?P#HO6>w9Mh71WQO8iweqy8=6H=oIMRxESXR}97b-3eVUuB%{_2$~jpPKlh(8l?_n&5g=L^ZzF@pkF zJo1LILyBroJO)EEkon%xpN&e|;1?b-b^GInZxQXxe>aI=cZ?B3=ALjSRg(yl+ew@ABcoC`u%wi}dYFB3rTIs~D zJ94#3@jA|jthT`0gURsM>&uvpBF&Ymo=$df?B_+?9bl#b#M9DpbsH-ml~u zbY+7tp*%m0UociZbr=@P8Lsvx>vo8fa$BZt^Zd~Lv-i%mNkZd`-lr3nhS`PI=)A-Z zNhBkh@f|~%B-TB~TfvRso8y;?4d%xnNK9K7hkHz?KPMoEZ@gOzi zgmmSEDmNnzru5%nlka&?rjD_TvtC2kwE2|q$F$&K9*d!eJlFf?bQqa-D0}YNOIwR! zFTV=bgWSl-qXT?OrxnDraBi{M?4vB%*U4LShkK~3`uwX4EheY-9gop8Yg+7Svelq1 z$L#xFG<%B3gc#U-9z))u3o8z}=(|{{!W<5Y%ly@!2}jpV4d^4h(grpK+v-;nOWZai z5=mQ6M-Fuf$Gg#o|FL{(fE2Y5Qfz~weQo7m`*i;IjusXdFQB;%{^*-cNP z7j#{3b}cZA7!aZOr|aBg#ND$>U-6qhv?fauo!Deys~bo7u$nyG|6$jIFqIt7<)CwF zd(5n0o0&gz8u;y6hUmT=G8c4t$1Hdc|9f2reGoj>r=X2Z3_He!Ji?Wy)f->vsbJ}D8=9})mRZ65vu_M`pOKN z{(8-$n)Bwvzf12Tiq!_#+E%t^wANS=ESFw}`oH5~-qLuKYhARLt_qX-u9g?|(sIoy zMm^9|QCwrmG=IZcMbF{O^`@R?9Dd8;qt!39iV4}INXK`$y*}3dv%i>5(1w>fsa8An ziV3P!2BJ%+ISq>j7)2TeGif=-MmkL%?uplFJk|W$2;|P1hEOftHUGyJrx(rQvLHUkNNEWwYC5E+K84c zPMj6prb2pya&${Q)MRXP@H{JNdvFTmt3JuIF%HJDwZ+@h<^)$x%t7OfiywtGe>I>t z5p<6u=QlBFr519wE_1^i4A z5%Nb9xiE!B#w8Yqnw(tkd8X@F#(7$?YBMnm$}U}_Kyc^JqrHDMe7pTevfGvPi|#AF zA;H3>TB%}<)L#FW9_fb6lRM->+Ri1lj0|FljtCLHTpDl}K0?Eg29Qif$Oax+ETQjbkC z5MS4^AsPp*?AP4smI~eSjZ;*uB7NvOofIjoOiMZ5>=Dzed2xVa?0dNdFOPRUN#mTU zZ$3!LWPnaisyuAdT%^9EWwpw6q{xkH5ASAY?LYsn6TkZg`f|_PbnM@)v?I`T zAZE0B(QBK(Pu;-*yEmg8wOgi1D$SnLYd6glO?6J2dSEr7%4bkr( z3y&$hb~z=Y#I$&hCWU)$=orOGJ;Nop$NEh=U#661(~Ap0Li`7WXHWA zO-+oVY9HMQ2pn;6M5-$G1XbCLHTdYgu%Tb*lP$J;3%srGu7-Bm?N?UsXC<9}ee~T@ zHxi_*mO54uHcPB57jYwf$c5gnkt>5`$XUJF^3mG>kFHpBhsO(>W26uVLc)aug*Yke zWZsu`#9neM;W}x`CzS6@!<+?{bPtj*9B;L7Y}dZH1{XhvQFfXu-6?MhSD!nxsl#vN z=T4Dgm5AXFtWhgvQq?g}(s{6r0!dNdUBud{+S!zD5V~D8F3#)wvsA4sq3p2n6*z-7 zC{8v`pZ((JCHe75>5owh$w_lQ?L?p}I!3*YsE2w5^sIkm>5yO$8elVt{1cv-8i~DM z{g4#_y(1?3Dc5NCmcOmq43`qel3AkggVLB--jqeTc~s?c^EYi?EFekc0oZ3GYlAty1kWf!4%Nqhxcu z1O+LjCr|dfP4?53k0zk72%ZCk-AlINc$c=uP#aeF$cC-Z)-MlHL)BR1*@%6E!6gO7QFgFq~WO!qPQVm72s#71*|9Wt6ml5Xm z^52vVJzm;V}N(n5;MKk97RQqg_(c)}xOfH$6d@#hOLsxK^Xj&GX;2 zO0x$q-fksU%`qEnoi6C=p_7L(+`o97E-D}VKT0cSXWunOd|Nkw2BGl>Prd)&S$(mX~!+ z|4gA!#;RtIo;VTMT7Gvs#CyRvCel<-@c_THTQQR?C5P|@~Yo6`Q>W{vXSpGuxSPUbbiLzn^1>mO=d z2YlBLF7{`Z`$v!=(#0&+PD5Nb$^$wKI!lsX{jbZYvL{b93fQwV+Mjb74p~GC-n{zu zR9zI^i;5@0~OGKc!F;WPm)LX&hKNRomx zaeDR%=yO2%5ycemf8NJUXhZK#ImahOOvS^*vf`~2E)PEzMlwRFZyvu?;e%=ix+uJ6 zC%x30OO6a2CiNS(=BW1b(-Ubw{vjy?Y2z_%G(#NDAG&SLefW;OF_dle4H!>$Mco&yZI_Eea*{qGcq&)RM=Hy_S>8+yoy;0aU_o>zr zss7Hz6rot06ifVpxVHFFHM>9`sO^*hqZiu)ELP(#pd`OAEg z7eDP$?-j?MEOwKgU;u8HpPd8Qsy{%6v&lur4TPGDP|pNx<+IZV9)<0oxwClfnR)(t z$VPq`j>i{UeyGUD)&N>7F4(3;y+dhyd$wz`2#z7>Ca!yws9?GV?N#6ROmiDn#O_@8 z&2Hp6e-nWI0KlHWg^6h@WMd(oQ@1daK5Kkufc*A;P`VPFw{XjSDGLiNVmOtiDgTG0U*$L#&`27tFS;|i16ydVONwQ;rdIQE&)27Okmn9 z7psx&j21@VAm>1PXGR#yx;)NGjPC6@kOD>2_wSAT_p$PxC8Cuxx^GM?6xAxPRZ#Z; zeV|ve4e${90RYAp-{&}<6zTsC&ragP#qC3jR+?@G}B>D~#$!K=}` z2Yi^hVsoGY#Q=EsE`)zMaF1b31NF6&598`pQ=`g=DInP5Ar-pkKS4k2XmVP=1<&4- z`xePhF~2Mvxpd(rN`sbiuVZgq8x~*n+D4H!r3O`OtI&EXilq9tJ>-2P-6QTIU%0Q^s7tp9xke(t`8Yd$Ud0uy4!+%Lb}ls;wZv9rtwN{huBSOAZi zD1ZBko9qQK{tG5UdPG76SnMi^HpWZ;eALf`rBi)(Xoj*S(`6$;pO`9aobpHH1tDhddncCz2d5c0QTEO$`#N70%O|}+fmOg# zBu`@1D$Wq|?gPLNz~a3*D8RCes|Y>E+h_z*+xRzg36r+KCe1uCJrH{41K{Iq?_qKF z55Jbw;EV^?1$Jhuu^0z!#K#`tOohOVgh}>W>GR4iy&9+0_ZKz=FzDp;AB7HHn-nkL zyOJg&cM-SmCd%hr60w`u674-@{Xb-VcRbba|F?VymAw+#Gph*MGO`JU%sNQ+-m?%V zdnX}TDeD*^D>6b+2-$lcns;6C^?EKdn9B;fKDDFm zG}_?J-j5tsX5Gb{+7^?Jol4z$bB`SpcSs_}=+-05G#`5T_17f=+QGaTh zEIbx{P0m~js<9zXl(S6Hyp0Ya3Fk_mJ^hE^aXO9K3!u}5oSiq}yE^HGuR9r@+ELp(vsA8n-7MXFGF=Y82#*Hh7KzO5`5{+K3N8EE|^u!b9wOWE}SvLLZFIqP)s)v zp)SdjK)2MlZfzVYHZ}k~#iOt8e65+*cw3u<9HrZfy-o-R)pF$)NyL)NPW7`o88zII zsjI>C((7j+=*y}NtuY?kb8~GY)y!0`gLLkf;$vVn_FJy+@=+@$>Z6u9-WWS(Zu0YA zz*;CcyS@&~ln};ImzhlL2w9BzZr!}kzJ93yCj#Gt1A}@)eNrVu;YTQaJoHOq_h;|T z<7Bwd4T%EY&i=y4usTn^#q_;15o6l8d{q2SFKCCDz#`|hV=8K&YK-P=Kj@6BCdef& z*WUacH%iuht!&`YclpPBud+SXL+TnT?%auqsupeJ8U1LVYRa^c;M8=gA2@MVfmGHf zBEg+2`XD(qo)%R6DE`rpB^XE9E<>1>%-Y8+O4;g5JIQbit zM;nbtJ#o&NAGnF%t;UFC_C};vrTB1d8}8nkOC+LKXQHVG5?mK>>}<<7EtIg^>ARsy z2|oGf?N~$#-zjv50f>069GSFTA2UQ$aiI7S!iYbpbk+i9uZH-L8%0!r28G;X(;aI-Jwp~9QdO#cI9c7@Qd0->)uqW zi>c|ym#-B@;`s3YyRw`5#QHs@ZgwjAhmN| zrrY{nT(6yu&+Q!S!UVd)AZ3{^wk9%Xf_S;8YbR~6sD->$f0Bv>T8sC}6` zyX24MlSUveMuoRLu+FgJQ)Yj5Vev)pQwsGyuw){Z0h*#H!CjK%WdYGU)CLk&BLsF; z2WwhaBs;n1i^$aeLA5AdS><&6UZeEs5gpMWTF>uhO+;eADR53w!6z1 z^bUTo-p%Z5z#Vqj{O*Nlyceq9fo27M;*sI=ueG>NLhqehdDG<4JhxvMP??dlSH~zL zmKBfzQiRHmloy+a=6u!>8@5h`a%h6RE9|{8Cnh7ttI*!3C5szEUC@oM3;u;GKy8Ci z9L~C(AK41gm^8b>APwn74Klkbr?I#3mja(Sb83ihx>btbxXi64PNmKLzRc`>t^lrK zEtCb1g_L$oGk;`gri-R2Hf*WolliHyh=(Uk zNiZS@2z(5#8^ss2V(Ub*o(9dEc;>~J+S!+tyt+?^Qw3eZTqZdRRV5N}xlkP^R6|-O zx;#yx-_pf;2h2#Sh85$YfwDONQ2T>cr{@%YGFuMLYnQ1?OFSWv&Ih@c$2DV1tMvO zZd0E$qg6`tiOS_>b%b^!Nxcj6S}oCej*@5ts2U>2nmy+vShC-LCr9|{MFt>h?u<%M zG#h*fs3bh}H~oP;sUe@8LxnSG`-%CdjH{wuw&reQfLWY=%hD3K@2;I3sSNUHlzw8H zc(rf4j1&K)llAzPQ&Uce`&$36{Uhr`VwSzDj_8McOf}P*(D2SK2wUnsJOFy;efjnG zKjrRE%_Zhe(S_wsE#v3?pgW$P44d0+3H#O*NyST{Ddrm;PKcdfjmm8n-=8>eS`@J@ z-gkbaTV@BZd*?0qv0hR9x+Z@tb-?l`-Im!~mi4m1*Z$^;;=9@ME^IG(T4hZ9{o!|M zu5(G%(+HS6d7ofE**HF>MaWaKfB9TdC*Q^*hMh8 z>pRN51US=|poKK+P$qAQ$i(3+D6cuQhrt-G++>TVH`GdI0HIApTl5Rd%ckOS-Fz6i z7a6K;>C!`$%|~OEe^avYj5p>e;Dc{*R9?$*&$8Cz=oR3UNE6zmo`Wip4gDl=+WiUA76GR&fK zOgT>@6g(jGPZMp(>hdVlK_XV-qYI|(1#Ke7Dy?;0>mQO;@(xsgotT`7D8R=R5N{Efka21^&Yb; zg`Xq?-#1YEhz*@A$_{hRCTehE<|2%sqfwPQgnIRHca=r)7Wk~hg9ay@1LOZBT6E<_ zR=}ChRg0`q$7;N+?h33q-sYXixoVT-3+k+-b!BNBtAo)ScP zruCv0D8se=rB!Dm@(OY(uRKmVH)P@#t$HZ`_*jEHiRnqu7mgq5Ny9n6m33M!wjXRb z56jy-FIR*=x18A^KvnN)nQXJ&hkGEbz21Pe&8xc`tYdEe8+XV{Za+`Pcd>okO9m6f;Vr}1)UyP8)r%3t8Ck&iBY73vE-=j*dss3*_uJl6GS;w~|G|9u(jsq;ug z7hITd&(VcMa3r$-m=zvt%a|Xy%z9teyZU>Utn3T&@Fqp%iL0Avp-#>n(O^z+|Al}d zv380cL71{BDE-ov#7uZ7dYgaJ=Q7~N`C6_T7)!BXYM;&k+YZeB z&Rtx_daGL=?j;V7GF(oo%xU!cq$(64ZFLQ01R4phyy?nLK9NNwH9-sckl1$H(!s+} zX06qB;G-lE&puUVke=|k90P87Hfnf~A#jU6-*9WF=m8t>uV2vHabUW!LGRaV-@KMz zdQ#`Prm@7$p_n|}@v#lg*yjfACdI8X7AX>JoL7*;SuPrnC{uz(sA#2ljU^3N|HB0^ zr2YUJ7BH4hEy(m$gGJ?(^jg0;lps$1cq8)?(7pmAr`?alZ^|s zU*bBFnzMMr=dIGA2B8xIX3>s;dSAeL2K;qu>z3g2^JDm1V z)iN4yA82fb=J1Dsi~QPey|&$|3<|~bwp^%!!5h;8hK`?K%}GNxO}s^nczaTA>xkQ^ zLv@;`@kjf3=!&m@NJ;J!<^{XX6^-h6WtojuJ&mnnO$$X{ zY-?#g3t`;G8H`F*%(SiQjp*25NsRit#NWo~e|{SQQ5D2W`+%G!bYux4TZKEfyg%-Y zJ-2*&ukFYQ;+on`&*l=fX+~P1XNEH(4{cp56Fg9fo5@CFJuI0l!<(%h8 z@JO>i;k3?zKenji1y@nfLDa5)TGePb8Ec0oP7rK?BUR8W({Zv1=!ZO6;%@mm=jApP zEYK|RFwk%vbLpV7YN0jv2Lt)jBbLVi{n-A;Vo8aqwdT2MBat1d5y;4g!Q{aZd5OE_N5auWWaL)|5sX)TnKGbkIIb{lSC6T)_u6v>EE75 z%7-}NN7_LGaI}X=LR+RnGko3;Ro+rpo(*1D<@y!~-?rg|FFLvY{{;5P_BGfJl{mC3 z7qQEqpG!6KRmIMXpm7R+h?JdA()$Ld)PBanKC}pz9#qr#Va^Gp8#_yz3>Gp@b_e0g6&<)&Jtr=5d z{);D$w@Hlq0^K!)axSyRX<~@mgS;NasrC9veNc}m=1>*g5q+5}R{Gd`YJ9W#0wR`PT7T>AqZ8-u4$~|Vg#(2L-&I;=61?QO zQQ?&qMDnVk!IMKMx0hvOQmfIbNTH8|eva0fu z3A|9xufC84?Y_B2pg&y%tHNb73m@D3J&e$-rwxDP9)tM!*2uxZ1NkP+`#82>u|P zq$G?wwL}|4nL|i!gXCeZJLm^&^<3h0?vc@3 zn-_WCcu;=&@R?llH-*nORltT^C=QYm))GZ^bjMWS^giA$|J!=cwsB2(Vh5~Rqj@4g zFX;F65q5M1(ld_Dk2g=J%#;~2Jlba4i0MMmTXZS9D`VU8`7!)1L&r=BOY8@``=HyU zueK#QSA&d?Af5j_*O@5Yku_3^`7u>bmm43o0cAn@Ez&&P!Pz(6(4Sj@W&iK4@od7| ztJj5*(ZxaWgZ;(r}d0wRm z)RKe)+$?NS3O8{FH*U>&UOY9Ht|=539k1$+N>@k2t-EiX<$fy4qHlBwaVC80?A@F3 zx;=rT`DT%8rMaF~-_LU8xUiYwP0Y2sgiyC_jg`y0N@Y&hw)N)Za-5NLnXi7QC6$9E zxy`>X3R|W_rSz*D%#E$5-WF^n<*;CEq)rs_p}-UjI1F3(974giv^OAZ(-3mYpi3$t z;>F}bis0nM88QzIMwZPl6HJy{^=;*kzMucyxcAS&<6f}^tpXaU`#t;~C93&`1&^+F znlm>kv;AJIZ1C#pJF2N}94o-j?Wm7EwimZT{nqLz>XnUWqE=3o+>(}Zs)Oio_{Gw0#|7rReYj6a8H??oAGI1{&B_DljY;P z&z|KS(s`F&1b_QG^#FuEAWpR>`p96gmwRHJ}e%wN1SE zy?t+|-L!t2a8&a{z3C2ifECBeBCvimXNLSc95Crw^XJ5{6Q*tVMyOCExC81xksgq| z-H?xqs?Oz~ZyqG_j*(+WY#c=EEuA-9vrGAqEHdK_4{$_fw25;`gb}g9zUz4b%Lggm zJ7?IdqVUA%!-H=rhd&z2#(GUe>srn}o39=Q+sjs=UmuUTlUQ=W37duiJPC z7R)rEbO1Dn!*$4)he!*~TA{C)@ll&^4ZJG(o?LK!YReq7-n;Ds;I@XtuJZxHK0#V7 zt|yJMsazEqV7sBvv4jtRgicIo7Vn{E*9|U~Pa1qGb1gw`zr9LIv^UtWU;el|mZt1xLk}-P zd)cV#rEZUa7!Rn7K$f9iF|&C7-hw}qZI$*Fc#kacbbvn=(_>_^#l6S?+qF8A zJM^8l8DdL8dFgbd zOLlhintOW0-G6V8LL|&tGg9E0VnhgYIC?QhKIN+(Nm784; z{BG*3$Z>N2DB;yhNVU{g4K|6LG2Xj?+Qmu&OJutj01 zZr%%2J6pp9e1)LKLv>c$<(tQk0p_58TB^1nGEIn zwYSaOBaNk0@y3W@vdq>g;7L5f7h2Zkz@$}3S+Ku6OvhpK01p0Ty?48fI(X;Cc^!fO>_BD6swZBI~=9^ zyk_fN-E+|fVmzpQ#+Axi1saqWI-@e}wN@)79ISr0c4{6g)F`K4?~@-!W4oWi;VlRg zR);tU;@S-uh=@jkbpAFL|E=HEJ)U4AK?L1R+$;@st#CHsf7ScUlUR7y%`tEiP=mrN zUt(>BWJ4Ld8|>h`65;Ouz{Y`DuF6c4q4#Kubs%V+dl58m)pyX-^X!+$NeeM*T&1+f z5Rcoy_t$-cv;(IT+sG}rX(Z^1B$7iRg%5ZI#!gT2dQtye5yL2f%-c8F@=3Y~Zc zFy6aSw$54bs+hz7F)L+D*?SSw;wL2mUnQ6}ViS^{*I;!VkQn3`ArqQq=?g=)1W%|)=qI8)1UU`krjih5oT9_CW` za$q_~aCuzKIWRosko^9REFxS^@t4aLQ_JGLEi(9Hn@V#3*J`Xy>Rd2OZitPf=_vN} zuTdBgu@m%b9HUgWi+@%?o22T-skW8SHfviN-FNYvV1%^gDF9|kPzHCJ2VK1)fgQ`% z9n{ELGwMz?710rOL7g^HlprAH{LDMY!5DYZ&!# zFe^^&#^_$=Oa6VsmGD^l{9KtgtFNLS)oP+)?)?Ber70&o(NVt20UV-o2DfiFw&6*- z8v~Ajmj!@TC!|>M-2c$7TOuW;riM;L`LRw4lOTFIxoN26ovia)Wekt5UC#jy$jiJR zPiumM>G4{^9^rkA43J$<1UC?MZ-3Z7&#&i5IPZ8Z4>I<=!&hmsMAin)#CO(m4zTG= zIGB#uY6MUWaxYc8Ck`jB1^#$*`*9bH9tuGrEZ>#H!xrbqD|@^dg4IC@fn&^XU_hz9 z5rZW9TIh5so~a=N_$BN45Ii5g8!nWEUJQ@zk;cvr+cP2a8z? zn>lX@73Fcf3=sMU4vq)g()iax$e4{!XM7^9PF@jjHxH7|rAtq~{GDaAc~87_pjJ2A z7Qe8+Eaq`j?;C0I?t9~`7)WG_11q|2V7*wHphv6hQZ4fR5bNL?!GkrxeeFGie7>{@ z&a5ff)^+;GfUzGweeg0|Wwr&kZv#>YbW>4CG;{U%+= zS+*ZK-UvDqCNctJ+~n0h$|DcZ-l(%e0C!7h=3W}eQ394wlk>DZorrA`)^m>0cPaawqqgah?++@Zo+_Zc;MAl*s=?(4T3a3{v2nzSkLnu{SAW-9 z6o~IC<`k(Z5W1`Bu*zvJ!Sbo2X4)(}58HG;_I39WpL1o$!ipLf5#}ZGweIO}dpfMJ zyfCZ<4Zzb^a@6@8_g=^P?yvKE>%>6kzy2GbCHhZA&p`quWd$GZlM#5gJ78QyfoX?5 zZ%(?#@|n;Q7c~i=v^!QCJB$(-EHNE@;^2~oWxLD`nFl39-dg&7Ur6)2)ETgt;T8H% zp+{Q>u6EQQ*v{ISe9H7)DOAwkOS8_rfqYP-{qC0Omg?7S2un)K<%e)BQT<+6*DfGS zJ$d!O;27Ax+Spm#ZMUemQ!v1egY7SCJVYwB87#HcO8HC~XXG0Uf1Zy!um`hLGT(C+ z;f1^})hl}7KgEGLcUpYj=1GQ;XG#_C;03?T$&eSUWJKp$Vqe{~VZCpAFP=y{+Te{4 z)w!ZQr+VMJw}}e@QG4zfdO3fM2GubV(~MuLZmkjEsL(870>NtHU>W=U0sp^PLuVhm zqlYPJ{(uoghxcpO1u*g{B+4e_`rqp$2o6~$KJ&@#RfH6z$mid*G9-Sr_9~>fc$ETU zQ1yLZy^kBXW5=?z^j!91#10}{+=gua;)@^ls+Oh>;&w_NLNoO)zGPY4_M*FomhEH z{)ivHeGn5iTvt_6Km@JYr3IYI)eZUEVDqtk#XJ;8JUQPu&;P zBJo9m%-LvlE!DPaC^~^2ONWK3HGNFQTde&W`v}_*#qaIeWOo+K<(5qf)5<6DH&|V? z1a#hpidQGxx)Q5jfSzoc^hWhSWVPW>EZ#&}V$>8Ez#@fP`k)|A@4}a1sBx`&Go@YI z>8!uPR($fF4orBzdRK!DRYwIb>AiXbqDKuUm{F`H`7?z0Jpfkf601zrnB&%484Vc5 z*1o{4>Cf8sLg$rkl%EVKzDh%M^NXJ{qa|d}bRgH)to-H()ZMuKj#^y7Qo75K6 z4$SkA8kY(HR=VQz(#FI0g)b5FKr z&0hm{bjt*1(y+qw*EftE$@~y#pSUne#uh1NHlz_+Z`pu83PrLaqLLYKDDKfg+U|BavANbZG#ZUn2 zYB0x`hhxc@EnQpmf>uxfoG3i?eQqCe4N+Y<$IgJ_YG(1NP{E0IxzyYjJD=q__ucgc zgQ!LFQ{%T&5(q4$3{1>a1v;;ZM9�W7=6yT}k+0Lo5h z&8W1FnK4%^v^ny=Tsk&x8kyM<dP<%_=<(d76U6m2JCr|1+kkWzs3qnlYqV;-Y zj_lK90zM(fNx8Nt{16x&DMRANPc9`2`okAtVb=G#TV^+IPcyW&_b`amJ1^n_+jEX9XTw{f+lBb-&L&ikF?+pzVCHVAlY zawsU(B4}&%axGB;! z(Be#q!Bk@oTJI;1yMFC%^XAxV4AFRO5CVc0*iu4>~^Ga{pYqI;9=~~lK zYLmNgtciQ=)XzRZU#j~2Ln+0-#qtJPJgXND$`g?%M{;ar?g;b4-HYEz>AYTlA(fR9 z;B!H3^yNj07rA_`U3g^|)&0Y!Amg!6Go_fNuwUZTEa(8P&u^5iNLI`wehyK%VkBq1 zd}}1`0h3oy`Wr#_H?Vk&DhxMSrfT|x%?fUHkGK6p^5T+7zI>Q#pk;_A$Mo$OU0m0u z`R*V}%6!nvI}{Dt_SU#m&(4>=B_xyVbfZT8GSW`BB=XZdd-L;E-hg1i*BXHsW%yj~s$pWaNDVZo@%i6>xvm2#(; z4|N9fbWKkS5{IPKR$)92egY{HU#RUwy7URf)i}CBbjRngZC7VvCbSr{7rG`Z+ae!= zxBVR*H6lKe)g;`iXBW9UQvjc~I=pRTmniuFp*`HG%9UYBQf3$roCgPz2E))zf7adZ z3lopnQ&18H@E_;(bj!`zS-bde!bN0anmku|gFBeO;Vr34W zh(=0tWNm3E@mzX9N}buiyT*>;#4NV1D9*Ma*Dh54< z(!06yv0#*$U!t0>CX!l^Q+-fSD^fhKi~SY&!N)n1{)Y>2&3H0kk}Mgj@7%63kJpUM z!3Ct9it48BPrn7YIGT6`54#0_*-NzVHZ&^>R%l21v_&FA7;jXJt-_E%$Z>~Zin+R` z8B%G-*=>Bm`Hq-~v)dI$P0_449L(X$GlJoyOpfgl$r%`iTQ2xYH-gg(JT7_|p~Zt; z$-?9$`7v6E-rEUV2^tq-i|;nCXxH1`y6qANuB5Z_SF~PA;>jc^sy$z!Ma8xmIW4}? zKdK45W;3pWY-sAH=kZ~@;8(Qd&4$&IgpzY&&*;`~Y>y8!!F8|DbRuO0a z%Ac(C^<|XY0JtSW#OZ;oz7t7yO}u;;%T{ZPvHZ8`g!ep2j7l}n+21kTAu3!MtF5R# z1;f4W!-?mQl~^%x{zdZKM4Q)`F^OXWJ-57MJ(bFl0epkLk>N&bC&P4w9)J&?mKd-j zV!bGQQoT#EqZGH6s8-)=>mNZc+vzPFpM#W@B2Wlp_w#-vZo0KrX92yBE9jFf?ONO> z9`^!emD8GIEaLS8Q994120{X61(^*ZW272<3`c4k3&OZCv!1;ZmDs@;XrgX=P%o&v z}$)y8VQJz!9uRDc5I&Ay`kuKyB+fWe@ z637{gx$jA4S|}~~wq;nTxA%SW#hB_$VnGQY7HE8>oYn|c zcraZXl`f;EmsHR?#ilPFyhts8(NBv^2)34-xZ5-56M7GvFTW=h+__D!dYQ@UO`$_9 zEwu$Wd~7uK)g43u2TIy98!&v%Krg79WpZ+=)j9AY95_n6fSaj{T4`=Iy>`m_d7}!H zJMFH$PARFSoC7;v#K(>FH{xq*c1GEnl=l5TsmH&y?qX^>d!$kfj^2~yXIUFqp89dU z(^+%V{u!c35}mPT{E;5f+Z?T{myN*s1CH&HlLXZ?>ZuVCabU#8-4j(KY^{Kx+kQ2v zsI&FE!V0QLvUoL$U?^aPU0DGQ92Id5#4@YP2mT?;jo7_+K06DKQtcp}xDU!eH)dk4 znqpV77_J}@|3yhjRha1H-OKKK{56Sf{N%SIovf=o&F@7wsNqgli7_Wh!$);8 zfco?8)pvt2tVLq*t401^<(c`;u)cRXY?Pc6I5WFy@(_LkHQnl-VuDU)Rn0afCNwXj#((?m)UslNz5Kg( zYD!}=wYp!U<>@bpIpgsCsi(io_i&smaN-&zYjzlJuuH;Jk_(f^fKK>9WRaMcdZeoN zKbnvCUtRbwv8Yfo@Uhb`25YDB&3}=7t`w=@Bb-@>_z#%g%Y~$CK{yp9H8wu!9rMy!5dB z+@Rg`)c5987afcvG&!6LOr_^$OdGT^!>dlCSi>s#cC&ZD98iJils0QErEsUfmpB=d zh5PpdkLN?}@0XlTYVu=FElRw0$)Db1#(dNlox_QYG&=PkW9D(@O;-&2;vpqcq4)3w zkECK?U1Gad0*n(Le?M0(k${d{Lc_>SOu!p%VT(9Dn!8K{G2Lu8mg9{11`R(N6dPGZ zyr-tP|8aFtt1CrviDV;X*wqUwo}Ekg_L>>nI{myR%V^-=)iG@ve1skEY6?jwNX>A@ zwtO~9mS2?9_vceKOy@%p(2t$E&^Lewb#x03VuPw(eOs2#g!>$Lvl^iC!@6MX%5jf( zGhuk+$R(>YBDy<(y%RY+Dr&fiEdB2FcK-#L?4aTXxhzceXFCKk|22U3aTq{kNumD< zScKQcc~UX!R`$gg=7NTJMO9`M(*$1Ha+_IM(&j1rCXR@A*bleG^gkjFiR|ikhKLOs zH7zmU>O+mTzzU`{Oqc%>9`ZK ziE8`Y720p{Q+WNpQV0ZL@_Kett!EAYkCbMRUE6K2K(p`1?ne^52;fM1kwWuT8ZSqaknxx;%@+6Flo!FQo21yE^7{hzN8R_^I}ZxJ ziK+Lkr)A!^ZsZ15MZ~r8km1Q@s{X0V*>zOC&72$|jU!V0d%$uuU+TPwFqRLDxKo&} zDY{-O*z?_l*?r-K+$)wTQIxi@_e^FCOars~pR3A&p;*(K4{b8a^~RB3lY&Z^>#X|# zM)nH|V&ZYD2Ju-^pY8q9WMn@_Aw_5GJt5MkFr(|Th?Rc4@>tva`gk&haa#D>9z*iG zcr&+yH{)yj7hl=(!`R;9)2)S<7P)~tzOHtQA6*w7#8ZB4b8B+9Ni>dXFu#yt$$P8M zO1dF4oWie^eDKsl5l1~hM>##0!XKpPC1&|1nYOK&9xQjNzeHcv&=5#Df)UjRRvcNGRTpzapfH|UMn3Bs!q(u=KyKg=Y}zenIat+iV3o>>xwTDbGg4j% zBzq;TZ$XGl4S8pIz;g+T9)F!p;V1TUpwUDQ7&2a-sZ?0hK8#wkLqIov*w}Tl!c_aO z&<^VBS9b-kioLVYV(-cspvEfku=3MfneRg;1qFn;7jVr3wvt)?xE08Ue_B-yRH zTGQ4Ze55H9!td>%<42jpeE@u4aouuDRqgg%8b9ra9u0Z`k|?~t${8c~?w5=Wo;fmL zCNeEWI`NLwIn{< z;<;KQ<#zsJXwJ*~H(MK_M=AH+vlYh-TjYU|17<~(%fCLPV$;k3w`2Eqj5oJ}IreXg zrA*f@9&C1p3Q%6>KR^8kQt7nQATW<8mK)@xTPqceAgRhREW$AXAQLC@jRIk~+`pl4 zzwy_P$W;gsy{+%uWNpywpcZo&lydpG7$>J%2{T-CtigR}s-StxNZ`2II_u|VJ22qi zhS%u^j8cQ=3#gyyQvG@Q};VI13V$da|!6^t2eYVFYkvk1pax{SLkJJGudF;&XYFFf`MW+5uQ#9 zbl^Nnk&GHDno6%#Xs}%LPM}pYW96Xl?~^{4#R55;icVmsz90--gsnsk1w_`B&#T-G z^1&27`=5T*QutNJX;cLEMGs4+hNBOh9Q^e@I{X&ErUxW(v!2FZmQJ2xmh(K4^4a+c zXDebxr&ll_(?s`FS2K$CHAi1OH|~qzi-bkSRa(AP8x9i+zeHiJUX(7l zL%T(B1nb!ale0{b&estE2ix=WQNlfh7r!R)=;cCfi#1BAQ@KKMq(rjx<*H)N0pBaT z8qJ$SN^Y7pjY19M%d82~=Tv%{3Qf<*eEIfEkQ9DHZ!}6{`qGB$8g!G@3{n~<*ONVr z%~Eg+sGRGLlGaK+LIzpGuX5@wYO4MgU;0daPDze@^Pi`^vg7=wPRp!Jjz=mb@7%+p zf^6#cV=Q%E z8={?CWGb@j`yf;j?%esa|7UA94)oIgf#=~PZ}<0m#`YC*7G%rqW=vmNTjCn+NB!r2uZ+ zkruyXce{Zo7RvozAv`YK{FVa)&G@G{oKk>tw@2(s)6jpkBBdW}g~QknX!QT`FcNzF z?Q$9rF<3B_ST_eglbXAKr*$`N5>)G}f#I7dX8@F>_0LD?EWm7xzOFoWVy<&4g%2w7Rg=KpH0w1(HVhHg^*2%Yc{vZ5nJ6$17-dlb#6MBBr_mdO#r{gLX$uDx zi`_qNDC7oK9Kx^o!u#dH>e|g;wpq_Fmgwoi>DEj3$9~beFZVI!@BRKjpQ(6)x##UD zb`y-fVTo5!>x73&|0fu3+ceY{ii#Ih$rOeHS^mAl)vS&Wtunr!l`k={^mEC;ofA}D zY47E>OWeQNg&mA*!fQ-FTTQ~gh8~_-z`=bR;Mp-i$=}YwG<57v3DlU6Zzp<`kqy;< zo?}kEo)0{eiMrB_x;W@t?xY(&@vGbeMLPd5(wmcfqoLCD0#^Kf*mgzM_L;_Ik=vif zenZT_%H@DBH|vC8hMMu-XszG=r-uYoR8!)(vyA?VjPxfjlWvuUely;C4Ts)z=45{_ zJ@j3aYXeQGQ1}c1QAn|#W!Dh>&+Vl(?&RU4qa&5HIshdD3-e`VOz&Ls{ZTVL8QTvwvFCr9-Y5~{zv@D)q8!| zic}E3IVWwBKjCL;Q#IdofMfeG%(Tir*&RY&)iTZH7fLgk;0-p~&*DEoBmeUY($8+3oMNBoAI=OKaB=2E0nwHAk^G z`lJ28I)iJ;CH7-%VjI5dF;XjEm*@*Rld+?{d+@n)MwawrZ#8rBC;dzMJ^?4IeGN`t za2aHj?qjweLlvqhc5(=%!p18j1n_o(=p@|VMbV3CeR5x(*vOU+$R%-tsRaDKA1Jme~m(&RLT2QJW;M-6LSV4$Dc(6NYXw=~U z<^hIF9CfVIECJ!R{m_4#XaM)^gGY@pC*k{v^rK^!jc2KvBxwG%EA;$-di;SkjibVo z7;~?yFJGai6Fz9tJvm%ar3)DqyN}oqeY-rKh20Ti_mL;m1|}>ZBKnTI3Zj!ZfKNK% z)UuVK7kAAC(-p~8kdB<{~C8T!34j$Q6izLv3*Yj zzTVWj@0?k9%u)4xFa6$O?iFKr>Nh3aVGF5Tz*S!qOqP_K;DGWid8?mEwd?X&VH1#oRU!A-;mSm?cbDx1s#%2F*H0%@2^1$Z6f04z(XT1GL5!(ATd22jt|2 z2(SdWB#BbIt)m(|+Q8&u`42b>CBc3(3p_jV!cte3N&h>p0jB+ZK$xU5;tcp6YX62Zb*pUe|D_y3gi>7CMcOXNnBl$u{$4z= zbFY-gZkmD#ye0T6{pG=k#zAmfYBy$WG|!~g6`M8EMXt;cFVacxSaX|3(2S2mJ>_N> zE*=MNS1?GS=s)=J{maz`{5QH`hR@E;q2gcMIx#7o=cSGdk7Gg5;5hGFKKteBe=ZA@ zR~&-BY%ohFw+!{eSl+2%bL^XsP}!oKjlY`(oz%*N!sEyP9YEv{fAgq;H-1CNPWqHO z(m*A4bUm({*Gxb!(@)6fAD87GHMA+NkisYIvdS-7qOiHy-(HLBK>^4CE-+nJE`z7^ zF?dv2wS*G&0b^k2s_I(VJ1D2kuvRKc?@^hlpreWNOTvxw3B`c8I1y{m1J^4*%;ZF} z`p^DGLg1?0Y-Q}03D^atgs8&^ySCI;C^itr?wjp@hGFnMMhDAE?KW+<)^O$OR;g_d ze^a*DU=A}v!<^xGw_iJ)%vUlD7|fl(a?e$W#q_PMJCa+UZ|+#T2HbevCm4x)f6JkS z@Xwio`zlrCLtgPx5?@JKY%U3^dx_1vw~Pex_=1~8#FbjY4Lm8qBC+;BSG)pxvkVaKh1SV z{kw!nzoote@!ywu_EavfFWG9N+Rtk1ux&eQrtR5{cej+msZMJOg0tY{qLraQ1wk&v zTo-TYO@Y0*y}Q4$GDb*Y;8eZpvX}iVIit6e{+SeXVVvz)$X~haaJH)n637Z666B)* z#AF%ti=BYgyutf>s!(vDi$qP%u>BFNjAIfRH~Gj%hxEM$_*MVHo#8bIyz3U$tiHS= zhHipud8|&)KO@)sL(}Q8dRK;9?J@9Z%L7NxcY3wl9^#??`yN#1yvUXU?Yth;?sCxG zI6YU`|1L%{gR>A)R#b6il$<`mrVoo$L?hk6Gr9!T1tDjA=r<9?px)7SNL-4?BaJ)& zItmYPg>L0$Bo3Q{+WwABFvYq3le^6#OubK^m~1&1QyfpPXp;m3Rzx_B7x8~R@t-aZ z-aNli#&)A7+a2`>TMAoyDX)q}8_RVZ9me!w!h@8mdgyDbC9hlzCE^P?3i7Y~V>a7= zxU6Toiy=UdH=HNBDf;S)@L^ymMy_a*L7zu(`MDYRKysThRpg%+}m ztRaddd&yD}WzSgKtdVTlWv$3$DTSeB>^sF+Q+6|UMvUb-@A3US-_LVhzyIL*bzSL- zxA)x3xzByh>zwmCO({;pZ8<`kBldf$H?3dDd(LRaQ;0GX10`j}GXoM)+4(MpaL22C zIfc@^Wb!vpI?G@tJaAaLKxA##7^0O>jF~qWuHd5)3sk|ZK0>KyUb5|EbB}z31 zfEP|>`_krAG7B->aP`Yf>>EPPq3-tAC%Z!n_lca(V%s>bCKDr||DxSWiuv}dTh?#L z93uM+gfBnJ6uha&t`-Y4AU`)tBY>vYXJBSQqQzxkth{B$tjp|F(pVC8ZCa?}m&<8| z`x{92zq~v#7s03lcoSW{_O1@{lFa&edc%y1M3vFXRhEbk`}}}#X_PS_R<+6k_Zc*A zbke(e3*gFALGTMYAM{a)-xK&;%jYl<-^Y0I)d@uLdIvz>v;!l<1Jl&Yh5NN z;%DOR@%$8b>DWXfY2Hggeg;@6M`z{niV~%&v6G}X6k1JdIBvOywCTFy-JBjsp182m zraiYLq2gWiFS90&B(|$$qTx#x|r_#c+35_+w0lh0@luR2N!R#Vaqt{Nu}rT5{^i= zc+F9dT2OY8M_+OW<>WNODI>r4AtU)C%gs*Rg;hymJi>COxQ0c;LLi>Ar3~5!dmm|4 znRUhCC3?Hkd03F{YLLCtvNT%`g#s6#zB}AM%jfkEoXLlJ2c}kEc5K#cmM^<0hDgV^~Sh5vfJ@CDwp z##ip8W)^xSU0KvD?YLNQ&lBxjKU&}#I;i7I`~i-So-Mv|Ly($NcYd5D4+V5~e|E`v zFbetQ2l^{^|aJG zp=MHxZ;XiH3ut9|8F&|6iM;Jzt;x;Jq`JReOJ)$zZ>n2`Ov@7w-*vU%XTR;VwcLP5 zZ|o`QEwYP1Zd?oB6`M96Dv+H)Fil(v6&H4ah_dUt>*`AePBLlYM~HqHuB&A_6}{AC z`IWw?_Sv`F>7PZvN9OfO4P9bPEm!|t@1S5c(^+=t@hy-G;~;pg#nkdsVy~TU!~u}| zfiK}lGL2@VA4+-DnF+7ed}lp4mmRlQ!XeADS)MQ!oFa+|ptIQ&)%qtF09mye=3`R! zGwsNm`_FO2#)5>$tCOeZbw8A-Byt~Tt5v2eSRRe}yEKSvUk$Mz9y-ON-Mz!Ep-4I< z=9}6MY=oNd(rf*u z-)xX_d#26aGWyoTFzSHkY(aLN10N7UJ|>9^{H{t?c+9C`5VU@oH+kh3S7L-o$N6ox zWO0mp!A`1DY`GtR-9NUI_VR;5#i-gwBy6ZX(Eqf#*P zMU?!ePJT*&-xR|^v=xv<2l<$l8&3W@5b4YJ;_6oo@=(3eUlCr)z?k>wJYC5)Cap^zL@Iern&iQJ<^NkY{xNVWv-#-6Fzv{|Hxf1rl1!MdgJ)X zNmITw>7}fURjtrLeoAcNl*ySOydNiZE|4dyrpcqeZ0Eo!=RBkDZqseXB1r~cD|HPV z9{d{ck#Ze85ik27`*Q_Or(3FhbIuo0sQXMu?=xfQ7}zu)WAco#Ms_52s;ZQpxqv@$ zfg2T#SCXrG>lMMWG~1v66wGYHzRP z{&5;(Dc<;s<*CiPT{N;}WDz&R>h1KA&6V`iu*c($WDU261B3YG{czRLp~}&0_MHPi zVcQ2n&+wVuMN@v)m)y)~lC^g-pJm6^3>-53%|~(9e~KKk-mF;9Ua%4f zxd4`Drfu@hOxr0g{kN{dEss{=5Ec4P)fCSE61DkRX;fd&YE(aZ(R6V@zr(0bUm)v3 zLv=ej!PeO}zV;b+N}%#1WYXy8bOGbW*f?aNQ7?22Rp<)F)R@l(l-o}E9P}nA4mxR+ zep4ZLsBAzFtQX4%C8M%t%7sFIS%@7zWja{PRTHdZJ8QjkRC_Od&JPdLs$R5z)A)`Ivg` z&srmyjomgxr5OS1o`-ZOsi*!M#%nV+JoTkNNj44H;^}9SL2aV3F$UGUq65yVVOak( zL2Yoih|y|GR@V8EdFPF7KQna|+`<|~IFD1}W67sT$-* z@z>0yDt&t0i+DB@ah0c(u1r??QJYHUe}3qfM8?&-wEIO`me|^Hp#y*<3)Fyw?XfOgvmTa(hv1l0(*(S;S36qAoN`KpRX@7h9 z#htQ}Dvkwpm;#=mZ)K`s_xBzb=DYYOU%YUn^88F)yC^}Ce*VVW5MrQkZ`hW|J9Aum z#dFR(Pb{JGIJE?FZv_$B)Pz&^h)10XwB9dbVgzcq8^n3bl_&Vqx>~1uOZ0&0R|HyR zM?oQvC1lE?p-JQo_e*Y)xuQp%X2#10?b3)tRufdcnuwHxA)5VXxhPl0`Pb*9s+1MO z4B4nBY?Du_zzO&F=QuP!$cB_m1w=xv&_vINT-WhdWvHt}p_$LBTnAGfiDq#EqI2Zz zy)T6RijLjwWqwX})z76b5eB+{JmH=7;+v;j-aZ-H%1^oBiPt~GZ{j%9J-cGa~YgKX{64aE{ozTfubD$rweEe(sqSQIT)>`(x9>yYy%tOa`Fb8kX? zBDfVAUz7sHuhwE^O~_kt)%4Ra^iUph<&H9TR>o?EUNr&ETKjnnYc;96Ag5acr#-C! z-YIb3YH5qP?x0s<7Bers0@#dW>^NzI3S~`l9$%mexh7FLk(;gd?#F=;m?F3JiKs-l z=Nt0ltTrcdQWHp%&ZZ1^Olc)f3nP9&FpU(=a4LF#LilsHAF@jA!G7;R>}clKKRhb( zi)8yN04kHZ|3{V5lq0l%aZ;MTKLTQG*mx{1l5ymf|t` zfbs6tVrDQGln+RMW+5nkAjitjxe7y-9+2k zNlyV0l3aQ-1ZgE9?xOgo3s*KRSJWlr)x8OMC8ChgF4I7LS)ezC|9zVo^F=YhUZS#^ z{H9AuPe?DtL5a@mF(!d|oZ#QwM@peiIO*sa>wS9_-W~1BX+d(xJ#4K}o(i?Br6&iTrm( zHI_;eq+dJ*ihRAHe0>TNEi7Jc8PlIVMvCE5=$mn%)4-j_NmEk#R8lAZvQe+F*p4p* z`P>OMEa*~5@L64~i?zo)jsoY*3n(h82UbLgUlBr81 zq0O~1rKUiC%{ERKbC&Bvv*ifH_UZl*LgCiEo;!{+``n(V3xbbz^5kM*d#{0+0Xl%< zg53`I1c&$o5j;+J#>{(M- z1|RhEXkDX_Khe%NJc^6gDs|sSa^MwmC3X6ouu1Xj_$OW6=;Rw+Hh=x7y}ugiFCxs- z@|$Z5^8mmmFgm!Z`vEhh`lQQVtoc0j^H7XGeb>a@68sq%YVX}3pI7IfG*+x8S%K35 z9lSz`Ih3AR85H_@B&O?dXe$IDi2!huH^bXlyP*Ik8e*as0DC}MgO7&0 zYu!O1ENBanh*4%UoavX@`Lab1&Lv~^gB-BNb#q!O2Vv`o>U&zspvV$Rc}1L|t|}pG zM$Xg^x~ez0v?$oc7L!;-N*65}Kr)|E}}8QJx7e?Im)O7E7itms=?&oOP)Ra~*YJy8C+JH`D^un_mj zn>at7$)d#5cM%HXTHqp4+sqGXB-)!IG;Mnp!_Q5h@bg3JX6l$)qMl-g$DS}U?Lql= zg5{a@1T&3d!w!VMM6pqSScC-;Ny<719U;X~@FuXA1Hys8{=j65r^3Z+j^g4cD+Ih(E#{V1#wsEYHWxVW& znk~tI@GiGyvG1Vyv(6GHOX;Wq$TEySdGqN?PRh@AC{nJ0(gjQ}QTVR)4_y{fK#`YVvta47 z6tm*Cj?Pna(rdHqn6gHolo^HfLuvEe!d@<68$KCoRTWSrMq?&x>xv)Qb5yPtqObg= z-@;zV1pscQ3H6LkOCAjnGnG|*Q$oJGGYDTIe_OnW84bpiUW@u8V$M^kRc`75ag$mk z{s8F@NRidcrwHTNXQ)E60d|PdE^fcI``x8(xyFS=h=REcrfh7&Ma(U@vYIz=7^l6@ zZK7}YnZE0L{&YkzwiS)PQA=I!@H(F6`?7T;x3mc}V$s6Z`jAMykn)k2j$V>k%;J-( z+=mJ4jlPh)^!fDu6CYoV5yk^=B|nQWT-<)JzihqgpykV7ozgao8O2&Y#YB==h2v(( zfbD_Xl7;&$d)rlP-D3PV)?as3PyX@&y$31to{RGnSD6%e{Tho;>gNo+VqAK7m5rKa z^VsvDDqR7q{%MANJFQYpq|&PtKg9NVaTR!oTB`00IKgq~r1j%4uG0xNf4PrY0;Fd4 z)A(=F2n1P9^W_&!mOj(Qalp#tbgS@alifNk?WSB}`zS<`1_S<$no*%Sz^>e3PX+lJ z=E@)pTe>{Wt%(luL3fl)ouy3qt6n%EobPjn;*cL7B12$^b66t=rDeHeT9h`j3%;Q)}P>HY`tCyLB}ihAb@@Gq48*ef)&}J*?WV4@z?q?Wgr~ z%6-?pTtq{-w?qBd<)SKO*1C>%;JS^Mb9*cMSJ!VUL^S_7+|CJE3tm3C7Rja1XVt(6 zcYUhAti<>&t^_}BEai30!CKhYHM1vzyC;WA{vc6xn8C|EBL5@vJiS*C{>n8ssp z{B11^xc6J7N}cCITs67?I*!+7e_H7syk=idQ?tQK3S=IiWX3-jgx<@MKYNX~acHZO z89B{3l%H`*Z$GI0@Nfh{@JjjOkp~Pc+E8Zxq91cBFo@c4Blf9Su*J) z|NZrVN!jW#kxBS4fkOs-(N-yr=bmg-|6>nnXba|x0YeZz=j?5<@60H3GQN8K&;0M~ zI;KP^m+4ccJw&>O7`P%Z zXf%As{}|t`8)%Ah1M?h$8Tk7jgnL2qc)NNw2W;c1qA<~2@YD%w+2E6Wlna<4cA6i> zEDz(E73TXQ15b4@r{?oM=Y#L4`NyxGwMu;xZ)1iG|Jy}4njRaRJ_E>I!^Zqco36m) zj7NofIFT9>R}IPnhIJ8MFB6bsDg;VPH!~Q`%SK!0|97=v(u6!IK^#0|5t(_EnFy9g zuidn2L=b-OZ9Yxfm^7aQn1Vj>HZTO{{?=8RxQidQ|Jr#x7gYQ2zt9sv+kQ0rHF>)W z&%f~=U`9Q~RE6vxLCUS>^12J;PTXJNtPO1lOdRELklQXazgOn2Deo6;hiygJWHe=Z zBrJ$F;=yqkafWod`d@GeNSiwjN`w&uCD+5ws=(+tUufb21Blp*(G&z=!=RZ_SIGPv zdmnoS(KGzBPYeMBcr80ha? z;gYqQ5w+k7q)kAEeQ>sXYr*b$rqE_j06AZ<+WJb{M2sM^c_(wj>O?`}jE>!AVxWM< zLy14v#&0#Fn!wCKLu)wT_k@_kCSQU41d8+C+6(us8EC?E(Ke5+ zJp|EXHBk>@VweMQ$kyUy86c#<_S&`EWIBFim#2AVtv!0s|Bs-a%0!&6+p zJ5HhWVWc_CCk%K1s05kZ3B$*Hf*Jj8R?28egmZbAf!+iDKRw#qRm+aGoNGUeLPnn% z3^E}&>q(>1C!tceuG^`$mGq!c6s35M?#^iDK0uR26C>Gk7 zSm5{eqSTizv14dQD)9y^%P#HLM&Fgmc)kngXvdZ97!3G%p|Y4Vh#r$TKY=meFT8!O zXoum&t@BIUX#j{=FTUlx#R0E4mNv(XVmJ@q(d2u0V7nDhaTpsbZS0;1u=|kjuC)l9 zECcC=Pv0=`luB$A;-RBtxVPl_3+x4BDj$OmKRoqpc7lsR7)EMBkM|ouwCn`>0^$)I)&88iERM zSZ6E6J%uR|{?J_&4Y+T})j1V{wtPh`c_Bi*Ot z{^X|p0>?ys92ge`k=h}oKT@~?h4}LOWaqoYeZu;x(68KsFas5|z3sV1k{#|FYb#l> zl}N^MEu^_#e_U$Uk|G7B?+SQhnLum`@y6!7v3=C?$vZiRPdhx9FfR=XzjzbU{>qvP zm*91w)%V&j2*p>xHrbIO8$tqH3?(s(qk+{cK#@>&BIOGkpJBcIdnqmkHCNi2Z1bm%! zz+rLQR|&VKVM}(IZ`;3J)F@v@?B@QXQTaWW6jv`}>;`VeDEh3vT;{by=-Y>p&Q|wD zln9CyO3G~1zh^Hu>;}De_(37JfR)ox*`w!aH`_LbHzDib)bshRcBBr2VRaxT4{@8F zn@)AT6j&7b0N76ycpHaq|KPNZ!J0pPm;Nh5BjP20-lbO$Bl{pn0E}HwUL!}DBoAPX2H}P~6;Z;#j8q&2iNM0}?2g^+SS`u#hwwcNgT=g8`fQGkbD0KJ-Fc#SG{n~aJ@PwKV@!H`602UB_!c1qIQ)uZU?g!=qf0--fqzLvqn; z=E)7$yLxuT8KKPwa<7ma@_gl6=fR{53Hb}j%#Ny;Mr7udF@AbkdfTq3DezGs*jHaP z4HOOzwpng09@ea%>Tduj@$50rMe?aw2;(Jk&Mj|wRL;;yUf@rllNw90ZU&6 zVXEhpZifP#bl6J{F`Mb=d620hEL&yMr45=EnNWbFv@yrZPx10k+ELi-u2GM4aI)=M zdS0HSro`}icA{|R+6dq?QtQc33E^C|->4S+B!un(VS6|{7j6g)9YPR@Z8<=NH$^vdT{k#dG`X+}>SmG2?>@LZdyp-U} zs^q;xYE#$Yc6gMWTC4_O`fi1zTbOYKN`F5#S)ptFcN>R1v`2%+0j?j3By!m_He%m? z4+U@CWydiwP|(rR&1Y4B6z;2;#yS-F$B+C$7_3DZi>G!%{#%mo210?|y^jm-bl z`i{OJq(W#l@^}eI?gC{Ux5p~=kmMaEumpI>YnkG51jW1K6NhN()ce@;w?q<$o4GSL zexIe?gdx$j+9J-GVw_OB2!jEqF7FR(@O+v~V9878pu?`yE3U#Qr(oBte`w(pg+*^$kBO?BCJ2$^F z@gU?W$4m9ht1POfTl_YP#yPx_w{?CWzH*Fd*P-jWCJ_?Q_2IMeRou7NIelYd_MLg` z`*sTsvlB#4^srxZgYZa;G9>ZP>>?N75w7RV@!ZEgaq4lsw_EHa2cT z8HbfFwJUeZRnT%dgLK2ar^|A4)kr$`DCvrKvQ3wMFMY=BK zch!jZ%J0s2+x2CL<$N{s$Z4oq`eoRBYL9t#s!O#ZuC%AbH@cOTwe)S;t+sSQ-)F|5 zDbYAlc8SV0XP0CX$FBZ2Vx$F;T3J?xAMWY6vU6NRNtNc%p^=Vmr^5wR_*KZ176?o`PYt{moBm|r2`UXP?K>aCTc({&5lvkwtXp#L1FfilGQCo9Lk+J5IlC7du|-$|AwL}!C>*b zzb~qF*>#CxI4A$d`=~8BS&x@72t0)aIRee!_{Y;gATJw;sj839m!1~eTaz+z^G
w3gADvXLuG@TxXBU=?c}Hq8)!?w7<;H~TkOV{=YfVal^@7fu}+3}mCy2?e%t zz?C2rCU%4syoy@s3`o&Ivz=z(e;W4u@^|$_g<-GbT-zAf&Z=TqHGdQ_AxZhA%ZjcQ z=d%wI6g#Ak1Y`sTch8lTDT}_QPjC(;}Aby%@kf;l6Yo}ZWw_cD2hQ#Rb#>tLq z&1A0+DgoOHSk)3#q=7YE=8djHT9A)b`D-NLM#nJC7IY-7MiJ}bte&~S5k$tZc6@!H zwGS$5@#$`5Fx|59#nl*2K{w>Z3>f>!PbY{MBtmvLF%^kHyt>WUi1h1jWKx{{9`A?xPF5ZN&&t17vO zJ3mgZYD@a=%H?}(BFJalax;tpoboGj_`|!bW{zr`K-d^CnL+lT#vXfX=mi=H7R@$E zaE1I^P_Yu`A|LE|=^6fMcw5(dT&hK;@h@8;bf_Z(@$K(ZOTFmi7*AJe#A`$)z5 z&&vy=kJpfpDq;=~bNvx!KC3whY1~nW-i?~=^(a;LTArC2Jg&c!6R;^BJ*H4l33=Wq zK`w&gi8tUWATm80`Q=ocCBOaE{1|X2>Y<#;ha8;E3%O_zHH#89$kxnq5v^Z5lg%W0 zR=W*NiN$n0+YjdE{>Evyhs3ftX+D}c5kO$^E_Z)a?9^!i>=d-zNF;bSr|!wX5YGMy zn$p?)czrp&tM_7(!et1Nl8DUZEC&mgpYp%^pEXr5O_&zkl1E^QXQY?+3h7!KSLo&4 zK8;7Yu7E!>)ugk2vgQas1R%xTCXe>q+uVte~&(58ot=U<1 z%t(b@O^WTP#d2T7p_308myR^>{vN%EVXY?(;LExVaMDXEHG6h{_#rAzNZGt2g}Ble zx)<+>1>plv+}=UET$9jg4fC5267F`vso}n1p*hZ{v4LPRyR{=~F=Z9nE!G1&>Ycaa zDXa8)^80#5mfMopup4D{h@&iM)A79kP2v8QDZF)DLNLR{-n-?X>H+}+ksh{J-d?GD zbDz!O6AQwZC#drh&94z24PlH!hi)jWE0&z0FJN8v)7tVLdPYq?!w!l zp}Y&0;JI~Tm?mQ^?pZu3%n|ozN|+cI_ZrDZTZZ!siL_-sN|zx5AA?) z#^@M_?=2|Vl{8KJ8R7uaq4H?TBU?HZUZu$B{^0>(;AbQIng31|BM*>_=kd!m$blA( zFFYb-<-~cmUwP}-kvCm~G%C}oi*|}&S^I1!-&qL4=f?9p{&%VYo`8VOT}lrZg>f25 z*~sb=2BhwlB6@La9&GQm0O$=#Z1#p5hq{aSbCexxB(2f+uc^+#ZVtUtJ!%B6%D1j} zP71=?47xJWmVx%z^%y!Ur~PM=S9Aa);(VXm#81h$4gW;LC%4A>2X^y+1!HT?|5q^n zzYq*g(0%>w|59AYf%kkraVV))V;$=_CrU$2frOBr@Y=O&B#H>Q*0pQcsB71* z598kezuC$aX}ETc<(eW~M#tOahcUrdRo!D#O<6i}F8Im5H9i$oG29mQfJ!Mx*5qvx zD>VAkfr@5v;$!QgLr#(ie6Dg#&T zW_$6UY*k=v?{s)?|7TPuKAiYE2MY4!F7+33@S%)01s?&OE;FQ&kA#@v&|a4h`sauA zbu1#0c#5{WNS$jOUJx-X71$$FED2mQXz_Y;mCpanOJTAy`ncUTJn6&cc&NCRo5@42 z|D1Rb1fLXGH|}r67qX7-38{xzVVQ|+lLk9**|+z>RdWY+^c{y z=Y%ni5nF9WpbDOl@m)QG2q4=$q zN4X~6yE}*Yt*!qsT+nd4XG{>|wu#)pT~v~jSADKw{17h%-*GDd2xdbP>d2W1`F;I}X z0d^!-!h8dn)ckuTK}xIKu+C!2eX4X~lqZ|u??OC*x7*>dS5Q!4?uT_gR*E(|QpnJJ z!SAQUKwlr3UV2wJ(MJzQT%T7TNcqZGvQa`Nemiy5;t4D%EnYw49Y2kBAYaU|qu7;G zD5*6p7j0ug_I$dTtFTV*8_~kG47oCe9pu>*;$s=(t4vNBKhFvOotGX84xn4k9BBkj zVocb*cwU1YnPf%W5vSK_1^$iF+OM(Bb8{@bH;#cuw2yRaX?QJ+fC8S#)B@i>``JPB z-XD!iZb^S8=Dja%&+kn?G#rhPRX;u2=F})MYI37K*1o0I8GSouR4cB7PU@vFqts`{ zV&z`!#7w7V#akhL-H@!v)rPZ(r6~r>lV2V_Udd7 zC?y>nzE%`on<&<CT){56_+$njb6;06@ z4fgUhrf#kx4^V1%)o{74ju-va*$U$*zk)!Darszqg{Go(WI;&bE-&b6DuSW*<`j3x z6!XGed!nhT%^vRWrnlZ}0zUKgN4`TD1=P;HT)6ADVowgPIyXGEg^=R>u`;jLh=o-e!DS_VNuHV|UFmse|j0PAR)mmw!U3TWf=QZ)Qm=kTOTL2pu^UD@)-0 zri9%!dGKrKI{nGSZC}e#2G{K%GS8j)aKl?76VGt`;^w@%&;I4})X8!uL8N=ktp-b+F(Po8HtQCOC7*Dhirc&R)(eS{Nest4 zm8~cd=Y`ijM-x9zql?9=5%i*eo*~_fZ$q9vg`vK86FAZeGczp4iV$3E}NJ{WI&_G0w^CT)56Lj23pTfavwh zSmqRrSu5E*Igv2k$>(GW2&sSe6Fx5r4@aGy-^gLwj&i1LjLE(GCHg)OM9WkGn2aSCSJk! z-!f$tudp$`dvU{oi#KBMJAYoWWdT9yHk&_lK|SN_Oiew9mKVr#FX*cJVx{pxs3E@Y z&NL>932-+YYsII+5m<81K0zWs+S*YvjCX{t)Y^^9Hxen0HF^D9_{u1BP_St0BPc(L z^Y(7@t&BVBVofaaPu_KkU?}R#r%Z=Kq;(WXH zzy;QJJ~ybdULZDcoZhZ;7;;cEbm!i^S0b$q!Ka>nA#Usas(CiCxypQPAE=Q@Sr?~+ zl@?|CPlq6IkEOorIz=vTzZyhEqRVg|NXz3L(jt6D29((fNDE7ZYsj?*0J$OS2r@4ju0QetMQ@|O;NI&YK{+I-^KG2q);>% z9YVg;RA?Tr|2d!Swk~;(GA*9z0XJ`?bK9QCk#7l2U3-Ua*I9^Zz$LrHiz_zw9~%N0 zf0p4T8pSVEc5g0TBsR}|NsczrNRn4$l)2)&`q}tAgv!0^7lDxD5BIITRZDmEmwq1_ z`*~5lq{mlYQb(UQ*voP(a~K~TIhC(RB60T_;W z%;riQGZ7CrZ~2!w7ZTyN(&Dr{_tLm^HHX?Vu}yx;@Ff|#>+&pQ;GAw?-MdLnhDn83 zV)or2J!xi^GWtw|u%2%_;p(P9?qvfBQ!IFc#G@I5+6_C{x3fis5>u99lfn6!gzPkI zWgbg+iOtO;FOPl(j{f56E{69n6VQ-Vn{N>bdC!^IyMNrtz03*B&fR^Ds_NT(f|tdt6ULM3f~GzVsrmn|xhJ33=jbO4DK8u4UQDiDR=hQT(cL z!8!$UTQ61X8RCt%DSqR_XmN1QxY!~;w9h0m}{%Js$ zhp3dR&olnk1AM(XT>d-g{ydJDv)qwbW)-<;YRG}=>daPwls=0a8^BJVnLxyw;fD}y zU~mL!r&3{w3JmymO?xW8p-jwXxQDcPEuSH7zAM&ng48)&g6W40bJ;~uS;*?woxvyv zp3^Y7x}zP(u+q&c#YdK#(yt9*D{%zdTWRkt?FI&4ZO({&d72*b`X~73H1;6{D0H(x zs-@9Y0?Zvr4bCsG&V<*|0=P>}gaOFCD3C~`=r`41kXtsk9dCtxhWk(^AUKrr%wf)v zQ!{P;UZ*cUu5&$tQEav|fM<2+1YCMM#Y&5TbS;>rFLm@CproTIo&MN4az*yg=bW)Y zBlu`>LP-a=})|8V0C7_^RyaIYB^e& z$H;ybv(g;q>$}+iXv<+2=f)|#fPDSy+#88vdyLfk^w8(vL&)5H565ILn6vY!2AV?3|hk#zd`hGH>w%Be`JhQl3=oxVV`$0tS&u%ddKLeJx0iwLHSXgB{f(XSLCNxvVqPt(Vo zsgUcr$BS}0({7xYqXO8}HVhnO9CfJN9tL!ckQF?|Q-NteH@?V7-i51rotzt{T3v)B zsWOPi6~txnMJ`?#?-tHxEGUJK@bK<)Kvny)jr}$zrw{CU#;6W+)UMuo{4C3RUAX=U z<%YfTW1%kd)O|;JR>YYC{%#1nd~?4}+{gJI{%JxY{$?^p1&>q<*| z;VSYl8QG4Rx+ilxIM?NwJ_77A{PaQv4wJzNynl#&C%qx8AU>U=7sOEJ`LWfMG>5WU zmP_ZwD~pP9o9uMu+9&^Dn9%rJlCoEP`VsP~%yIy1R&U@v;PA6P%vEm)LRI<) zj|~{g2YRlWBbyI*p>Euyes1^Q*G;6gXx2|R_%W00NX1UIHgQy>ZIph|Zo`?z8AbIV zV6TSuY6ki9KH?n}4Cr#(lxEy^jH(ule4c7zTqL*sJ%o19qBbK#TtPbL%Dp+EUFs0K zP4IwfuiBkR(wgY$r!w9x5(F_&Evn`#9!jH89kl zTV6Ls{eMB)+JAY>G;dm(BeCsTT4&bWA6;y3_Rcz?L)IxjiL6<{$zi(y2>#7|Pv{qU zu?`KAt>86AJ#IJ+lLcO)`O7ra4L`7-8@#h88S)9>`*z96TcrP`LZ*DCjvQ$$HUFE7 zWJff%{rp_nHKAtVj)$BS%UE7ElN<(w=oXuV-$-D9yJUIHntt_tzj6;NHpmx5@sf{8 ze#AGg*CQfcwoS3!Lr(bm)KaMZ-RI1m+e9D!slu0tzzO&VCySF1V8X=p%CjnvNljVX zR>!l7Hu-W)O8dwDWoQ0i%Jq)KwSaDE5Q1ro6wE+T3o(Gj!6*I>l~?Z1{m)QLLC^et ztpXd+AXZC>3YOBqPYgJX(fXs{}~z&hAy&xzm5e5Lxao|>~3?QG!4stS^phs`iG~F{NJHC!NAHa8s7y%FzG@A zq44b1U$Tg~N5XTJmQli2$f*Q}*zsi%>#^D8`3aY4@C&8?R9=}ZCjZYU4Gt)ZCGnNKO2LKcRl?Id8EZsU&4cTM z=qBxd=#bz(P`(SgDfeP=f>VQ);(;nH7>)nzMIc+9qi;^pmqzQAXJKQ}2qmm0?_G;g zr>U-Vo${x3Xv?PJH!UxmzJGB1cRgA+fq{w}3N|0Ify$`$l;Ha?wW1#JJ*=uz$8$67 zA0zUzb}-aatf(846%|{V!disrmKSB3|CivxO#DCPA)v$XpGLb|*PhZ9;>dE9$M*&@ zZ2z%~OIq3BGmS2~t{(aisnv-Mwk;9#QMC?`RcKG;+?UcSup;}TlfIWMXA3`Z(!1n4fy9bh=L^q=F z(?4El_V;WaabwOZSx@_$Q5u39=Uz{iAj8!zT$z=r`PPc-TUE!RC=Qw5586_ zMcD0(8%tj4Mfg4ahi3dt2lgvgNjmtQ5`0jDD8D*YfsI?el~B2doa9$=K)*)q?)wB~ zXa1#&K+wbtgBahZp8XGTi3ysB2x(}kY1$Nmt6_Jay*pLP=AWk{B)}R97BUJBV!_XY zEzUG?3Sf3|e1pdq?p5*}HA-8s9LCrt(tp>!g<&Z@rcgGrAK<9EpgX8C>Wp}KF%J{P|HY^%T9oh1 zU~r)UZbv>le)C`U}#OZ1w7r_#BU& zbC>X6C+3Rn{2H*LW$!s&pt{l)s8nv-_{!XxkS5Sk!3J8qGj7avOH%@E7j@|1*5qKIv7BA z2a*VPJKPvWW?yyRl;T~riLpYbYKnhLN7y&OeDOcPwGupjDHC#_780$YnT!9=hy>KHkaTflFEA2G#UPe;gGX3Z(}!^iHp1=d-{pheIB9jplq zX{DnVy+(`;2AcyeT>uO=Lq3e`gO6c*tH9cVKbC_n0?3c42ia;3cKYpvhFApmS4%$~ zaU)73VT}Q-%s+HW`{T#>TX`>V_kFgoe>VyPA>Jc7$|}+Jz@Qz&l8V|& zc(h)1oE595N+#_Y+|Vt3Jmg`j0yBQZNe@i$--j=WJU>~_DSo~_EaWi7YroL?U?@!> zl#ufFQYm5k@q(XDsa|=bWfHe;bcTqdl5w;9+Zev)+j_t>m4jVbcpw?}ze)6z99}U# z{fzQa#zB7au6@NL3ykt$x0f26^tb<&k^+j}|6Ag3RYAKsh!sbhf1De~Nz*?%LZwdVk3FGEqSt3AVPlUn7i zPKa(zK;4b%>UlC?u7#BN|8+E0wNbE;p?D^VjO-WtiX~4PDp-(!Vnx4X4!Hl_cvX(( z6{Me(Q7m%rV6~ru;X8)9^!jO%ao0TLPnqLv{qmwsmIt*}G^J=vQk4mH#;h17`rm5} zskl>)>UGWfJE?VxYR?Ozm61YmWgwnGYXUhQhG@_ckOGJ%MHSvuamN+&7G`@bA_-8EOw1&Koj95SEDHevIqR(b3&c_eMTVjU=)@vOU>f6Y~6FVAv5Lp<8KU9P9V9 z?f7Z+A3Bfiorc8%4%G~e1SScbs2>Y$;%(>qgSED!Q1iF7+=&puI|fxjYOn9J&Hx+d zxgTz5y8d+i{bPDLfbBF>%)>r~?-$)_D$e)-l#AiT<;nU)jkPkjc7e)|1&$}My#1$U z4=6BXcOY(cjnWKz67ZdJehNkBKdugc19ff)@U=2H`6^x*1m15%FKiEs)E&*0vxEx< zUY>_ghM3V2_0b`8MzSUB7Tf(FOEQQ&qm|h2V@tYcGi)GL1*PUUd3}R~KFM)TElUhC z(%~1M1PEUUFl#Z$LCoyNS7iY&bmb66Q!%xh!d$pR}tWQS*FCQ7V zcpj<1zRjFY>YGOGnfmPryDIDti~pqT763skGkDDkO6SUmuu4{uzYQ+xlNZ1xf2~aY zjoNTrVACtI{+xi*C^PfWqbk#mfQfd0zkyVKX8(gB(XOuh{-^HdkFg=IsQW=%gK+R$ znt&xdC=<9V{x-|j+s?6GgYuX;C4Q_*cuHG#KVSF4ABv zdHk-R5j*IW(t#Z-JzdAMAG`!6jr)8uHG4xB>}~?p_v*_4M@e8fS{m~x0|5Jk>?K|S zknL^*B)lpGD9R|w!_SXG?q>&_ZVI$|{rD<#$FTN|Lq(H(WA^3gc4b%3^VxdGt`L1t z*c!}(Mpv}Ndx_(@M=Ku_I1_~_YlB-hz$He2utml9u2&)i+1Wq5D%rWUkXI`iQugg^ z@%|0oLJ+!g8Jt@MIA{>xhC~-tj_18{s$7(g4RwN^@O;1MBI1ucF>^ABg3VaQv%E*C z2qJYZ@t?m0xu~LiK_eAD^o0MiR$e1J?x!T+10ZmFK?>y61GO}4>AO%9Yv zRgA?fBc5bb?$>zh-h(GaF09<9#SfD!4dMs%dDYjrlMBh&(~ZP#Xm{Tb>&Wp4;xW#C z-ePbMdHxb1dGP5T)NCk2*ui3xd->b#j@hg819ewU&4nx>RHbH~Ji)Rpue!uhQk7@L zlLoa!t?7|qY&?tAF@vjA(*VeB0fVU1r~Rf+j8QE~mF=h8y1*_|D$aek?w*Fc^V z3|nr`r!wzZXUSm{_jHHD%1NuLk5BhLGKU-apB_T==1&gRwI@q-r|TwnL;H^oX%y()Yuzv|K)nJT_3w6uD2_YSyDXt%Y2o)Wq!^!ajCOgpS* zueV_~VcZuIGrZ&O>$+Vyc;nu}!QiS^I(PUCa`@-EM(~N>FL$5e9g89OK0i9(WGz+Y zUEN(sM6a3n_d}X7#);^v9yS!MvM(FmM_jH@4@#+Ci7){#tA`J&IfI|@8rf-nvdo+< zH*QI(XxSATfouKd(SF5pjV$EsaCbg43O$eaYI_ruDvHkAY@*x&fvnL*uke&B9rt{{(8W_pitKaCn6d>>*co`^+~i=+$wTHnHOwj_frA`A7Q zJGA)d*U2SuNfa?l1*)0wKIhg$BpGe$t|q_-<8>^CcyC8!K6UTX?q%N$3lk+*KF{pW zH`X~SF5J2(9EqJk03aG=1yvRE*|*|=W_7hq0v=S$x2OuiB<`uw*awx4t6us^M@I)G zEIBv&9{-_DzEs4P|EJ%A|M9%Q$NLoc&^SeO<6|xJ zdu=AUS_c8jEhb&<&j#URnFF}hdumVP!iY+1r88{u?Ag}B5Cdm~@171pSK9DF)N{WR zIVH34oa~@VJmS@Q=s$~HxAH$i&IBrAP>u@PdI@{qiFH>oBXozQh!kP4GgF5v&^<4g zuAqgBgdghj=zk?)5YbrajobV^>zn|QRV0U8pYp~VyDtUL2!C;(ILYg_b?2KJ>%r)U z>5)IUV`k07qj*t`QqgZDJGFOXx)!d){l`Il6m1}V)U5{ttD>Iy8RrFcu!3T7+2sFS zw8b+Y%|-9`Ss;TO6Xn(|p5FR!>vin6qZ2c?O`M&jZC~TXC3e!Ma1N9T>=i_@bv7|{ z@7;(bh24EJBqLJQYiEXi4gq@PN3_%0{tD!PO}oT~SVBUtuY`Q)Dz6K;IPKadXkwEw zS!pV=$YyaJ>*Cfn?pA#tAn;)mLouFyi>c#;LPKIX2hZsK_(TZ_fm*)YCaj_ z^i#|2UAB(nC!u1W7cmOheitpQIsK9u4va8BwLg{0yuGaol`t%v^&8^bdZ7R=Bl;z@ zqQHH7D(?5rY$}OtqfJ*=gpO%R6pZ(^zL9Hq zs~i=RgLM^H7L;1!qKYaC#GCNn&sQAl<;7SNF+_j!=rgIK>9~gh4Fu7)cbg9V*vrpu z+WU8>XPs%LBMdD+!UcJdAL2v}t|71X${QBQ!^mMocJ~r#w7$paslr%sd0!II@Szo7 zaX|2m=9@hTR;irI1q<^ZR`tGjy}G>MvKfBu%1pSq5Z#_~=ZU;-i4Mf$CZiavEW9x~ zR7`Z<{gsPUPpg2srTbR^-lCg-HOeX_tY`bFeF#O!!_1A_j;5Xz`=9;p8sk>}dKOsd zOy(GcK6LJ7H-!6!p$w?#O7sJ$`Hy3?%|c3?^@QwP3B>IwKv<$*TJL>g$Tm-gn2)lW z7z=cn&~qM%swnS<(X*uDKS_|5d{X}m(y@Yj=yUw4u35II&d9u?a<~%|#n5zwRCjbb zlz_|XozfL#;vR0p7bXec&--hGmZRnuv>^Op>BLMB9! zqni@&NLX?a3yPeNwEN~Q8n>(@?LxJvB0aD>YUEoCTvJoKOSAf>AIbJ|0|00WojmO? z{7?c1s~kT6yPmJO9BpQjVF=6HL8d1@#Mi< zdkdFILHQVC04g`cj7bG`z7?IasDgbQf5gP6X3$5}hxAa%c)F<9sH6*8bU3w+cn{ub z_zWQaZ2;lgAJ(YAs5-eXGJ!9t0!+hkF2X@9Od33NyDx2$3pe+!$B(a?X&FPflKmr6 zSFJ1_y;%EE0*irH{tzS~jZc>0gHgQIp08C!^hD>CFG(4>Ri@>iw@l-L@}H2B>jR(r zqIc=cZE?WS!;fS5+tb$4EueI{xihzB`2F=f?pi#juP-h3m3(${qYCQ$hY{;(NyXWz zZ)VonRGB{DGO`d|s#G}S?|sXZ9_}t(77s1}XpSY6A9(M@+m4IhParI8OzANSuu;X2@-u(jt%QRfANBF9fJqtGR~N0rP{M-RT2 z7{8;pemD~a8Ytfv;m>}TZJks-c)eoZ`pt{@amtP6%F2f6Cg&mF9=PCq6zuy=nyD`) zeyVdHariq~?}wZ(Nm_z}0p)wIIS}j0K0OGbBs$P$@=;@Y;WwjVqkWgsOQH%ExXUE` z5V~sHv{OG@)0m|MjXYE9iJ>WcABr~)1Uq4KarDB_u**t=>fE)5RuvECNMtNDU{q}< z-!pL`IMy{%Wg;ac(*UHw?SulNzQ*y&+{nCgMri6&5A1_n{1O;PC!e<12OBQ*Gs zU8MIAuXm_cWp034G1;E!g=v6OtL`WHN%;Xjj%kZR$V-y^-_qazXVKkmAahCc+AkAKRfiWR+lVsHcrgH{anoiO2>E3CZy z=PA}*8kRPDk?5rN+%IF@tIrO{)$N8~KXC77Pc^X5Yz?Fvf!ftY$)h>{=wsjO(rphlg^K?W=7%~2+=x@XcR$v72%A95HAduygvn%0y z=~{L&%-w-wX*LE9AjMO*>}6eFx^UWpek zdODP0!S@{p-4{Au=b$fBu|AZkU_F>Zl?#CM5&qU_0!UGn?=b$0`UuG;0s&-jySEEI zwrzj9xZvzB@`OF^UP!6KXTHjk?3*2W5u)b{iMrM8zsAJeRwK#K(!p5O*L?L*{moxR zS*2f&J{p)+nW%r-W&thM%WD+PAq!%!NqYNJgTPt~dNRo0Vbw+Qd~+;6v(^VpQ0=dg@9UrJlq_|y`GF)k;9V3lKNDcdCtJxALpBp;lYv{;qesEPkkhh z7`PXnRx*u@o-0%r8E zF?^yNv(<5}YGep;f27r0*>HI6a?5UW4B6(Vt~dsGXL^w#Z}0qApukX5=@kxC&LnG-L;qtRg!BwYNN zi-6>=cXJ)WkgEYmgqj{hbO1qp=8JE!vV$u@#lXeCqR{5p?ARFcc^$6T|AruccSNCi zGiU2KJo6}7Pwrek*oy-CF0b#|RdytQ8wb^fgXjf8wU2d>ecG z;>BJMO|AEyh3oz3qNU!Jc9|_JuxR^d^Q~90*%VG|cC+>6)}N&tE{dH#f;gyEr{)^i z&Pq1IO?Uq-aX4ZiNX)x<0KF>cg??~1!G|D2$PUGB{Zbpsb@rtFMUzY2Jq2~)`1J^c z(`$}Zvzq4 zONq4r1}>|KTi~$VHKJvoKU5d)by^5CiG)3T<6;)~hYS?AdJ>G*7u6;LNSy+eIZZNr zSdG#R`P;9^6*p(1UwyI3@fXQFyU}io+{Kc5`b7_C>YC;~n)HnBKTWk3y$)>SAuI+r z5iD8>Zww%cKAhyEP^9eYm@@wm$D^7z_iLO%mw83bIvJ~PL|y8_-LIp5d%7tIa(m|kAi3KdyCbTm6Yk0~Ah-LmP&G3}*xovcGkKxr3oO@{R5qR#?h1gq^}ko6_&u^y0sTMfIsSJ)0IR(aMiz=fh746(?xa z=lZE(2vMZlq>}M3vyxv2*p9M^Yh`}LT^Sa~*BrHvpkD`E;Co9lB+!Yl)oAebsQmSh zAANO4&R7I7=q>UD+a*6DnN=6(D-a|o2}-(eZx#fW2O8Y-tBH7s3z~J!J=pf)*W5HDi%Df;viEd-Z}Ki7Lx zDzU@$$Jc717k^wrVz$5@6 z@IhW{1Ipbb9(_Jn8KzAoGGaVj_&fW_k`(V|S4TMo{KoMTT^??ODl=YV!b{TJ*a?nE zB?qR|JE#&J!-Rm48Sj>?@@N3dshDbhHumq_m zT&@PlN}-OCfR`w$0zoQ9{6TCM63L3S8q~Vry%Ogd1+$VjW5SuN=5Bm<7nBdOEN{IZ zWT7Da-u+(%L~$0od7JemyPMNM7hEe{&0$oNjKP_o;>eP1O)k5rP8FD$ z41@vt$9Jw_0X@`KZXqx$xz{a`!_6^;MnS)7c@6})*R{Whfte?szpwBMCg+OKs(mS2Hz_bd{{7p#eHbLat_7#Q~o4R1)* zZNA4fVFBlR3P|SN_jt@O7`9jX1w;scg-lWxt0$&?HAq9ucvC`k*dyZ_XzuGcpv)= zIr2DJ{7bmnFHm;U{ww@-x!KcxVd7<}hfoV{z}ok55Xz zBDf-WwdV6@BL>y|NkK2G?a6V?E`(Asl%sf{F z;Hr}Vg(MgB3t3GWZm^89DQ-KL0-P|dp3{M)KvVyt#5pB5X5B!;Ye((3!_b;(^VA zI}Imz*E7!oe*EO$XN!9|TZh<^>?d5NHgw*3#;wyT^XYOto`4-g1+IVToz2worBH-B zwV&;_$n6TRngLFL2Q(_A;l5Ml=-CKmFMqvqX70!cO^($pcqbK{(X_!SE5Ee#)dS#K zJ>4AXx?^95X_FKtPj_ePlyPQG0OXfb+W}n>eYkbcX^=G2LvIZO78xeUbO5LXbXq(e zIh7f0ji-1(1CcV`Cxl_5rlGp}CtmP}OA|XLMCBMKJIVxa>GxY!Gpf;kk+)W$5jg+1 zG)4G4;&RvyC!)~;R9cua&)6YLS$pff*PQB1$&Y+?c>tl)y!~z+SJ>mt{>fcK7=w_l zbYErJ0JPY5Ve>OH{#x&`Xms&q*8HdO+AkHxJZ^AkrF(101WV)=Vd?5-Va~4ILn(&8 z)KAKfnG9Mk4wR=bm#YK=Tx&==+Rq$XOsf&stej-Y!S#1l>>i+Cp1bY$XsdDxyutAf zKjtRUDySsjv=;!@jWGwFBZZ*3(R*3Hk>Fm=A5CwbvX@qFo%}kBx2o~^wLT4zHcSU1 zAuV~>C}dY!E$yz%93TYrEY z16UJpj}Z69D*x@Jkk5-Ql!y#0Xq*-v)jQ4>`5b(ApSDgN1#Fx$z?tz)SZe8lat0h8 z+#o298RzJ3N0h9G=GB*&q?cVhaB~!fzXrdR2GSDJ-ht&l6h>d>3L7`tt9Z;$ZrzaDBFdv2c2tKQC3!_5H9}v2n)8%cR0vf z*D$?X4hz?kle)d%9FtPzp$iCh6hpBj$Xra{$VCD#UzF$+ql)-U=P)g#J0`7Z70p}R zy5q-l>dlU;7h~)0idW|OE{*(A-Q>e;o4c+m7^>Kz*OD$w9Qi%>^unedp7F6;oY;g4 zv>(E+=;bEF^)cxX&cTg#k2#{9{vj@%_=Q(9RA5XBCoI*@$J~0kN4HSUfSY2HsKf{_>6156C3t`)B4?5!?FUW=yMu}@CNc^VN zElru1-Ll%bD9hXh)zMg%XMa(7)u+nG?;<$VvsD`Xyq7-^47LRA`!N>| z>MxHb{)LzNyfHrjG%(#k&oicPuNT{KdP)Tbm@Sn$`^kZ^x6} zYP;zgYn^0Dc2Bn5R1LSNgGAsJ3yQI5}z|3z;#yXO!7!mD^{#g-A4FGM$kL*TV9BY%l&MnMpd z6^|JEf3e`(SapalnAqf7u&wFdsQoy22-*-0lT>pbf3YXu<9yiKedk{|*~}dwefuIn zvWiQ_46jkR{~C#!kQj(Wk*c4lW^ggCZeR+x>1lBQDdzF6jB1ux^04IDk2Jqq_?#lq zf5dl#^vew=`?c^4MYE2$A)i%X6s4?@Flcs2q_O|eq>$}rmgfBdo}Tvu(NsJiT1Z#b zeW?_&B28T1L_DH>c+a`*D5Y^3OLTl7nP>dhx9*;A0@=bwj@QkFZjmkn7@^kfBfH4Ua;rhOOuv_^s@yia`MjQYpBkL&|ylXCGnZI0woo zVW+t?&^4qxhPLWIeq9fWd`#IP{u?Nr9n#`1atdzC^iKKP>dLlgzUl;-1e7SsmwF$}zbI85Ddb(1xai4X&_ zT?x=hNuc#2Oa!R{TJ!CfC z?YNU(CsSg~PJk)b#m<&NevHjF1^=Q$QX} ztGAlH03HUKH!iHgOh5YOkN~(*P-||k!3a3!?AO}l_~C*JjEbKqxE`@kEYMi?#zORF zZ#{5h`K_|51PhP9HSi7pnRT3s5(}V-AD4kfs|ifE8o4jnzLzwXCO%f8p2t8ayTPe^ z2m@EWy~|Kx>=Yb$Rsmk3mCB^(XVTXUeI>YTx<)A1i#ay;8JqzheL zVwqNf9fOYb&V%Y2{DFXFAFrjyt^abV)&8LmK*yVI=!{&Hx*z`D^KQjqeF(!{(lkOU zZc0TpYQ?vq-f!h!5Wf>16A8A07C&7J1w=ry9w?4>zS?pgi27d~eVJCfEIUo+p99pq zp-I>=G$ymYMAXPlVy+Lh>j?XYnbKGy>o)lPb5Z?g`&mWfyU<4@r52)e!s|$#cB6W6 zoukWFUaj9k@V2o;%D|hBc`liu;Ws)o&5YkORsi5PQ}gv}cI0w&$)ZDa4^g=|&@{s^ zz3^RQrBt`3a4?s&0~2c`jDBM7X^|NIRrT*_q|9YfbW=TZ>F!7rBVLfik_^5A1#2BUAmw(w7(z91pI z8)LMK=kCtszTH$*{?h=XnWpw49ZFvoW%Y9)BW+=9s2XQWxD1>n>?|)6zg(}TWU9l; z8$GjL5(#sW;WL`+Io&F)ECK{B^c0B4sx;`wX;?P!0<6p+i~`nx1K*VA2Yt&VE^V7M z&_Z|P*=6mTd_sP2vZ7q?y{A?D7KT&nO@=|=VAd9+oj-L+p5kMsxUX;;X2Lkc5{K`4-Vb= zFwL|1jrC&fLP6vJ{(FZdaxwP}jpkL9a+ola(|pzNqDwbPbzcc+Ia`SoTuG}T4CIlL zIQu~#zK`VOpi3Rve2>U|W&3Xr;{RsTPJuyDjrS6K1|}q*rs^JChwj?tlDG{+s}c@% zmJXSnY7=H{F=BdWWeO?_Kn>1|l`go*$(f$RQ2k}dv2ZFc=mlQ@IY|@i10c6CZfP%> zlIqx-C=aCCCh`($tjlFlM^_*Xvdl&>cqpp9uepznU>~O|5>fUYOE#jT`-n0Tb8pSmmG<4?I#L!!hRIa|{3qo7%ghjTYH4Zgx?G(10ji|z8k}G{ z`@Qpys!F}NAvXaO-jUaWpF*PofX+b<$Q3D@ss!}OzJT^II}DXr=``OQ@@ZLg4C^gW zw4TU(-^IMTfEI1dlO+ax#S@!JhKiz%7*qJz06pD}U>!x+Nj$p6h9^>wI}*0~!T)Y! zArN#u*8u`A7WlCT@#Ifo({Pfi=it&uS_LUXBa~7u*=&7U<3Ga3ctYk(cz`_C_ep#a z88!!Mck1y!E{TT}dcxKXOXQBUYK0%|3Jodr6moZz^X=$1xqFaTzEE9sktT3#hj~%F-YohF7xxEXg%%IJ zwr|nV?L{1@e~I6wCpgScidpEnduCg_D)Egwd=8%Og$9w|F8T5(|C88pzJ))s&&bHs3Yub$Fsd#g5saj4@pxGRvDD~xGY`dcY%sTteC_G8x zp%bkv8$tuIvR{>f`J#eZEV*!6puTAs2FXWrG}WDz4{$8vLr^Tcq%h4(N#K3;yM$DH z@Sg!SQ#*I<=J`GyP11P-C&*ggf{S1TH(47ov&|xru;u_% zkq!=%@w76=S(dSIKe_x&;s)lO|GuurgwcGjcuKNdD_u2d^}O6YvxNGlMP81)tbXPJ z%3^AjO?`}HML6OAGc}Q+fSr3n)>aC4B>L4xYmHO;ixd3*&oU1^c!6Vx4UfjD0KX?? zOmrr*-uV3X?Z!1XXs$}8NZj$I6QxX!$QZX>5sMSyT7={) zTlB`6Ce;UpBfp^onDAz&Gt}qDR{@})O-kEgOL!=I-{n^xxWw3#8mD2pZp@Ii23iRh zzykEUCt-2|*?(9fprF1T;51g%+O?`n*z?>>#4%?IV{FHaVQI z3U+Z1WcGf<<6AyYrg*@cH3!atl>-&<#(v(0kX&)Ddhl=$?{abP6tC|OuO_3EwQ_zt zCxs?Yrh14o*a3Qo3eoU?_<9e2EZ_fcJhDetk&$t^WEGK_E#tD1y-D`WUfC%m#T5xr z$=-XfL`EU9XI56(vbkSZ@Av2ae(uNj_xSk(F6VKa$9WvD*K-~A@QoK#uhA=VzO6EF zbI_sAgq<(xH0BH0k9u$QF%O=`&zw%O@^m{d6#;1<*bbDM5 zRj6IsIpW>0ImK+Oh-@}1n84yN#0K1dLzjYKbFmQ0O9l)`b=w4ajB_;9TSIfX;6eMV z?gx~l4UbDF_hLMUMks)V3aHiK&?c6K+kjJ`Z1R|>lpF#8>m1(x(ocZ%3vYZgTADxB zQcX2NdPI)0rqa}ZQ~t{vqkYH!4&3RP%gIWm)YvktD1FE@S_?Jrar1s87(8!-(bbs# zQozkXuDja9;arFsIQB6%ic8$Os&baN8UmSrf{&K-4a$}PfTeaW++OU>%GJrsO^%pN z1@VA7(zW_qW5CfhZ+q8iud#Bw`!3ry3Xwe`b0l0wmDd)!;=c@bxgV106)%r*BntL> zV|U7F>J5IAw$)c-mX6)nbP-)xvIq?*M+L9 z|3KrikrH>4y-;Xb@9(oqo$P206Pp2{Ne67*C62qzI!s_R`Iu?(0Vg)R63o$qcJMH3-7KV+T)KoQE(!bMx;j}gdo;|(dt z^tktkwl{V9E+t07Pm`^DQhRi}8R`oWXoqMgLKWSRFe|93jR#@!W;JdZ`!WUq<`?tR zT}LMiIZ?6{VUYp>&^I&t`y;nld-`dMJp1KRF)6XuiPUgt04W2m{T1RE` zy}32^PdX5oLvB09K1-)lRIr_le6qH+-1zO;TT|}zP*jW4C0&JymjZ!`2HgU`-n{hc zvq3B~#g^CM1rC9C=FaTdb`Tdi11k2J+K$p zrR}q*X?dr;uk&ZQp8VUKR%-k%uBHZV>@k>5<6+b#NLotrd_}{ zN5Db$zRM>!m9Xgn&nUUbbyqnayJ(yce{{s3C!{P?oyB_6-#2wAm6SejjN9UZty*Im z^>f2&B146SPT1Laf>|v4kyrxNRaHPJ{tX%8QS8T#EQt=dBJSJVr@Z;OkHXl_%)mRK z&qQ+a5(i^o^6-Pq>&cRt;=XQC{*R)Kgx(L+G$<$r-jdBwPDoo_H7oLrQ=fEw!4CUl zWFtl5F4!v4K|iB1?;#E<`7%<%VZC`Df7mP^FZBB?nh78OobtAT4p7MUTo z8n0)nlq~$9XV%Z&DyXq6lz8irE1;XTb92gDJ<9TA3oKc9v%M1dF*HsWJ*DD zly*`vDi=c%Ca7^d{N`g7<+x~t<*US?9>ZvJEu5(+a6DMh!NhCQ1g#dQl&{Qw5%>g0 z_@F&tl?qcIH~$)|4LUo0eZ(dCa!=U&iagWin#IsX@kfE#10he-eCa5Rc!jrib zojk+u>94;pIr&T&;=0@BBy9y>&}JNlI9913YX6y|pS#LwKD$JhfkJycx|0qr#^ZSEbUtr%e@Ww&yb^YI@T% z3zGUAwi5`s=V7GFf2c{J`5}w?`2=;y{KU(oRKDL&Nt;}W!GqVR^4W7HsWrbz3alo` z;%V_5Q&SR-vb-!uiT4zQLv5v09iEtG-=an>onk{4$ttla(sDCawLs^(S$QZm71}RD zOal(W>(nWVNKYT^*QnEYg`Qr}+^KuitfRm-$G_?ZZl$=xtCwCy zT53j=YTrZ5lkmDbeeR_X%r&|Rozb#fmbN}J<=LX138oA``F7pQY4`l##if_9=XPGa zYw?AX;otbSpZ)oMsV?TeV61uUunSvQ+li^WjEACsM)z-5fV6sn$dF!P?+ACgQ)|3jMmmDa^(h5!L zGyx4St=VI`<$+57o+$hvH6q=A8N7j4yeDMHBzDLzacUxk{1C&sbpahc`72dUS!-45Q-1Y%eVmT7^KNO7qUrF`X)ztptQ*r>LI|v&!G)sm>h$n zOyHq(`bf5+&KO4Aj4d}M^Kz>AERP#IpItCof*Xq?^XcOXP@ttc96Gsi%n8FL-X_0p zk_*KMx3=F%03`qW*TkDSAb^?u;3LfVL22}j!?Yu12po;=OL@G*dwsqYHSED6M zHBlCmQWY}$!_en&IcpX7i45IU^eKhX^D%ohB-B}4`7g*M_uq9?JUu^*$tjBwt_NK> zhL=tfaZN+{f~8l(+!JS6PMV%vSx)-%wCg=+2_M)`H%fqqETTtzTU7g^rUm4iGZzQWCm z6lcPUc(0su_}x-B-Kz^{@Y!GYR|uxcZ^4n4!M$R_NSOBV@Ru4Zt<;149Or^i)L1;G zR26WWN0rpH)tyVWHX(+<#0X?Z9~3u)c0m)=>4g8OhID9dO~v~EOte`7A5$;&!VavL zi{ZQv>NoMaQ^fSDiyDC$7)V#5ihYs=ife-lRvT|%Ck_YNmNxH!RjkKYcs?06@vN;l z!B~j}euZscXkpDyIyy10YYW}U36bgC-SY~Zk-_!|&ZcTD%4@0GBq8tC3#&t>RFN`> z=mU8flAHYetqBxcQC@2kqF!$`YcoP|5bN0`JdI29GzqFwpCHvG|L#gfi3-w_5PQJ` z_YEGa`&iS;p$w%`2hX}X#ys|_U4QqD_6}ehQV0+})c<+U8XG4^8b%^VKAi$PTJak$ z)FMVqMi&p2tYsc+LGr(Dw!VJM%Te+e&NpDq|IkTfgnY@;`ZkLc7WQ}RocKH1dI-CT z4*JOSL=NQ}8&=HQFOsnM1D^za_%W|d?OuGPOrCQ0E`HFu@K3_no8Nf)?$+vGxCI*s zz7}D|3PwQSnm3sL$_1*6$b`j_Sc|wKX~|xP%L1_^tZ!53V<0y`1?hUX=E1b2>;`SM z5A}7!0nZ*(Ug8o8X|D7}DoB!)QT%n~gXuffG`;{we^(Qpl!I>c{dSL-y6QsJbl?X# zDZYb21Dz|hrZWX4*l5JOflbOnHd&0(y+6QHf6jl?iD!dIBw;xfcOu`2LaitZX!APO zI@No=eISmA^lX=S<~sM8aF`b645x4D50_MX3cEgXl2WOr#PMZcQepK9(N%OVlXUl? z!+84jKD(^G{Kq!~*w7;HMxd&F^@#M+-Hu0O7T-85nq&-+e*?#s`@{Jezo3-6sJg{$ z5j8fwjI}`QXM;%J0Q*6XK7D)u*uzs%*4EZUQG#pz&QRe;opbq64=C+2XiGk*@YJ+! zYp$Y@3+d7vWDFwAH<|?C@NrW3WNR2bXjtLxJ=94gp+^s*~jW6ON;o08~Du^yQ ze%cYa)@3Qj;JD|Y^zk$DoQ#aG8nt>*aO6K}h~(2s?7BS_DPV{!5L)xP20OBj!@I_l zk94Dz%K@^|Y}oD36vb)~aj@`kRxg7c zmXwx1?3U*Wa!uuDPl}PsV<5$@LE52lgP$O5wNJ?hFD--;^N{0TC{_ju^ziHhf;BME zz91k@F=iDeO1~K^(2GB0&`KLzw+aG@Y&fuHpU(mfdhOb46frvgbtJO?smSu}sqbMA zx$7!FD}s-dUrqIUFUAh*^LJPo7h=OM@%LTn1n&?bjdsoZS0SG1@|sftvf84?)m62u zb_-)q`TA(;DszYAP7WUly3OGyhuveAip+529uh)$8)0&55PkWl(_>Bbolbw@EgEe%$cwfT`t=oa}YBIxXBy0GH5Fv1tw zyRQ(C`4)~}o<YyjRPa5<*#0q^4SXBNB!OY2M^P7r9KF1 z{z>fM;*3lmO)*@dJG{Z(_{!PXt<2KHNljaCDB+OPlQNdgVKA4iHJ;*GH$IoOf2f;N z{G4)Ly2b_eFn(J^EmhtuZp zJu@2B5F!>YJndh`{Gj+y3UXP)yjj+CXOHMk^e5H4#@a!jhTC}tajmv6<1O-6a^%zf zi<(aTo-N(in6Epk7&iT2MvWpQw8jxi_y`U0M~{eDhTQ0|^Zxk6VBgn-EMn&*68t{0yXs!dEIo9J#k zVPd%d`U3&IiP6GUE)A0p6%)3J?J^OCg$2#cj`1GKm_;P!i)5i$^MdY`*%_&vk_s6d zhDA@9CW4;=@Y~Z??7L1w6bkE|}xb@>78r1vn!RuGUk>#f;^GDxE zOXZ7yy}2GqO}08-WY&aoEki+8UFVEKAqJHus?=wlj12jAGr-J)>*Vn8_^)F-C#&c< z*0wU`P}J4h7q*`G{V4HRX9Ytf)gW$-9JZ4ZKMReJHUGu{1Q8SgT&dMD{ zhnC-fq`1g!b;Pm`l-a2B{rL9&cANR_`q^}61ZjUPO4d~W^4o`NqAf=b7_reEjtgBs z{`ieMRS^=uD`{4a5MVCGn{B|2Is^Bea>s7O4ofNW=_|)7vK`!2z6pK4pGFTF*a)mZ z3cL*AxR?6O**fJ-7)XAYEu)^S|*acrp;xj;r%ZF?rdznIsow^gctyg|KOEU6LFDi*frEd#6tW@0_9vU#4 zD}T)b=-`)pv63yJx?csJZ!vKL-2ZsK4u^mzcN5^wLq?>FhUfIjU&>ngxx=_@AB*$6 zGatQ4kfDblByIx})N<3=Nl^r&UHR)D5A;7loI}J8ZgDpA?A6dGtt#b(rspSjK>{R# zh^%{oI^YB7ABhLy{?f0PpCzuV-VW9%+&j6vjW%Yli zS89f|1?4v0Ym77(#kL>gvVc)ener(;F0|HSNtXChVki4LiU2|-RgDLN6xix5ylU=Y zUGmQV;6h1s+o?(GGIWy$(S^A#C;Y>Lirhj!cA)fOHvRd=%ss#%wJ z4phQ1X+P%i-|s!g=-u3Mo$5>YcuY%iJpFcLRAduv8~@*YDAEy;0?!2A(R9yYn#l!~ zCl6xYj+ZDX`+n6`64o%v80S6|n2r)qbP^nr5BPAD7kg2uKN4rA_%*YAUdqy^JFXWV zqfAW%Up;SImV_L#ANNJTV(0oI4{VEWCG}O>5K2x-7xi3^<$eS{<3#M2IL_Ne z^;<+H;261MPLwZ6)p;)xx$^*o;Rw1V5HqqM4lz`Yezjl^YotoVFzfX~y*7Z6qnW9c zijvxXS4d+xIxAE~bd{_7#6O8u_Kj$1p42-1;KXaRbu_J*Ax(JvuOXSF5sI~aaXKU` z@>>93!b&d0vOMWnSRR6;%BpGV`4ZrpXH)IqNi3)pa5zE*DMLiaZEs&y#W%Pt?EEI; z&^ErVhjS=!$Ody>x7vOqW7u!LA8p(DGt@&1b%0fbT{PtQqOgED<^-b}o|Kk5ThyUR z1WaKk_x6OT#ot4Tp@<&&;KR}Pv3eOHO658KZXC*f?Yi3B2SX@PBX|epTz?_lgAbcM z4?XXLI*y(UBRg-O@G8D?J|wQ8js;yT(vq>8Xp>5@$29ZA_FBZsNWHInE)as(Jfn_{ z1HI;=h5KP!;e6sr>@A^7ze5kB9EScPXim z2l(Gn9?1VAKXKcI2<)vq!I<5j@$>U90oHwqx*ZBKq)L?wRwERikF9cm$v2R^tsGpp z-k3~8eT^d@9jPfXq9`4Jc}NPSrSI7qA6kP6KI7=wHAUS<*j-m*1E*gd#TX~0TFff}u&Md7HVYog;R#*2%lq?`+tCSn{JHug@#DWA0xN45E`2DEd}+X) zAm3MLNuHzbr(@F9kIEnmB`dG6wrkJojw}+MoRi*&HNY%mi4?E| zKL_fFR(NF}e|bHccN(~7C{}Wa>nhwY?l8GfX2>?VEAM>}+IPJF;WF8cXE^<;#$F8j z$RWafdj(nAup{pgk;HcI4|>)P=~URuk5S5{5l7EmEaW$+3_wk!GBflvB44*KZ|k`w z)TXwxt&t(se6ymRUaHbgC94;eznl z#CkEM3*>>fy_$jlOZKhauav8s`d+fiv z800{Z{gI**f@tnSw2u77)jC`|8<)5JPQ_Qx$I4wmrEeA+{h{hhYvQ&^3Y=?-JpH-r zK3Y}s)-s^hZZ1UQGdt+u}s)F-~?HC<72lbnSJT6?+8np2D%s#VkME{=!gOFvzg0r(RqZ^V?mCC(VWa z^Bd$g$$z`3p9rbfzJ99(Rbd%~&Lxv|-Za!|$mHP)piNB^0WORr19GImaoMG~7#ys( zTxY|6OZNW5v*e3(xM-2c^{cr%zceJ^m<#HwRStz&It4k4Y=fVBc){0R~`vFF?j8RvdC1@B@ zsN&jPsrCBJ)v*X2ua%jdY&MXrA_&@+Ep!O#0&|+PoOW|2LtVrYuaKJMJn7c7ZqUu4 zig5)Usm{-L{?yR;>U4iBBynCu3o6E{$rtBBnQ-1GX9A<;8zgmjG_PiFQ$S^K(gNkM z!4fQSC7Z*~65bG{zXWbPRb+hgYrhoW zCMz?|onoLqudj$7RL6&`zsR*MCDFfUyL(~Hge*QzOFy@coF+brP8aMOA_&fP)csb= zJw~fZFxm8j!dw0>qcGc@0o}T=t+NOMOEfWS3??!upBxjWx|4bNfB!znqYK?95T4`& zs}ewI7>54^Ju`GDnl#AY5vP-9KvRhLrdwa?Fw8?I?sEg0HD4tqo3yIiXmPX`0hfX>$imtHK(F-TVmU8=7C6`Ij@1Ea=iNa$)EKv~#ffCM+Q>|B%T$pa(OW zKeeX;+&Fn)&5*tz@RkAVf+H*^Uck-SORwgx#-Kj4#yQ4oS1@z-lL7i`fExUc!TkC#1Z>8T=#`L3(WSfUIu{!M zVZ4jIAc$_YXClW@3V9Mw2k+SimEdh>qVCfrZ~{nr$^Rl0x}z= zJ?obnp`*PcKj=UBZrzM`GvYI}nq}lFF{)P+)`nTGHP~RNuE?Q789=j(atjp^cF4Gd zo3YvzT$@KgW=nk*4}F1IU3kuae{>rn1~fl9Vw6`9p0tL1BS9mk8lp)x z$?;{iqRF`@1UeWRP&e&7>CBLL)6)Mi@CouvK_6{hSA2yh~ zC0?m3-btMf`dB|aA$5HjE)FTTrHFv7>D}BGB@k6}mhxME3@~V0bL@H*W*KU8olm*03A8*s~?DEtHs!13X z&UG74KsQB+rZ&ANJ!vq~_y1f41dO2(c3}Y~EV7{hrElirmGLt=&LsUc3LC3w$M-B~ zPYcQbj=2(&T-~yyDtV0&!@b4Zd!$X185=4^=;)%O!>cc+i+VkW^NM%c$Pk1o1MsMA zy->!touHbcoaMIQJjkRm^^vJ|2b}GH(=vzu6-1c2F?4PelMT^2Df>ZQ7zD2jRF+Oa zv*CF6L-gKV2r?*}P>*v62}Tt){7EXN484}>`kB_i5@+*#A=Gr;rT+SQ6W`+s3m|H| zG&b%4P6nW$0?Dh-?){zV#s8UU*sx!s($?GScHTpYO@K(;v}M0e0HFopYxIG2>U#R? zN&uGd1m>yFNle)@c)lihdCH+rbudb^= z&YN4s&Mk^g5JM4tnR-Mf$q{;irIhPN$ao-dK{(WKvqL&`mwg}y!o|W_4imdUumCDC z*3(n)XA<#GK?WgcBFIyGeWG+*{{eLm;X(s+=dY^-J!z#AD<_G(qP%!>lL* zKHV1wXR-zvPZmT82w*ztmP?T3W|j$kq?VK^R5x zvZf`LOxU0cNNh?Db*Vf5wF%*j-u^}(BV`~>hoRa)_L>g6FtIPNN=CJi14XbEoDV^g z;<`+)x&UE`dS~lWhFhbqfTYSelWuoeeXLUp&W4g;am+BE!ui9ai8V}Y6oLWvH~xF^ zl7ym?3ee_M%9C%&CWVi^-WEcU=4M>ymT4q$q4K3?+X*uobyfnsgY35}b!_olC}lSS zH+)3IaU_Nx3d2r!Ki=QOF8bf}P^4HalsfF!!LaTOxkQcu!54)bNABw5&A3ICU)xy}U{75nM~RI^{I$%sFHN?w*f-du0q_o*oZ!{R1rTuEaNXzc!MNI}Q7QJK zcqM15LlK136ZZ@a*ghdJFD7@FH-C`>bJ8aIw4M0(G%OkFAI{>(?u0%Xue?=L*@6}U zzu=60ZNQP&OwYsr;)5b5X&$d~PM~7xSPf*pG3rwkTtoL`C*_mHC>%EjQ~*YbxJ()P z6Uhy`F=BWKvNt0|TiU3d8<~f|x=P~ueNiN_2A^cIH8^+6KhvtD zZs~&jPC>1euZvOMhB;)Tay$4^S|Gw??D*5}$OzS4r<5Hr@+X7ak9D!#&Dm`Dqt^G{AG zsLMw!xQ1cW7-0TmSkP=MAvW!2wlN^8c&uA&5O#VxA9Q&0dBy8EmjB;ZhD7Usff&zs z1XlHnbHIdR}xe*+PpudVLr^c8( z;{g`KnX36S=FoN`dP$x=%Ci0-n5_9Vb{@mT-5KXw_nOa{vxMPN*~y?UaU&v8*$^De ztF$+48GyRaem&0A#OP(lS&W7eKya*@(HA-HLOgS+Jb$1w0(S>);$zis zzL#>72Ip0X%z0$TQuLyNBFJGti{Dd!c6!uyau=r*{8KKkmYXBaKZ@G9(fW|aIg?pG zV#NMhvtvgL3sk(t`tKOH5rnBO7z-pvK6Ciz6XB`t%O8LcE8$}+IUgyEKOU=LNg<8z z&1?S7ZFP)m5z#INUIh#PC)d#z%Ef7c^(4a~4s2D}8^i=_8Mh=zwERJ+@~XCh9`7Z= zFfq3UY;bZTR$+(od`9>S7vI!=aULtjlrKDP=VfYU)}uqSHDj10r$r2M3@f;(l<@s1 zGjcKCMOy?W2_k>>pHIF1{ZI8(dp*63XYKqP1PD=xsI?y}&l%YWINoeW#Cl$quQ+|o zhYu0k?-?guKl!Nxo+N`PDQcS{4=XXOun(2~_#F$6^p}!MZVdJLbvp8o(CyWlN}EIg z-Oqm0Jp1G6mGld#Pe#H&y;Pr~X|2!bBRCL}*=mHE%QcHK?C#oPNqD#l_nL0kz!i`e zpqnu9Wj=zK+{!qqpg{BN7Lkb8hG}wyA9?S6I{<(5V;ztAj#vN$*f&Mp#6oW~7a`|` zYu_uM1`8m5Frar-LT!f;iT!ev#3bBTc^@;{_B42=qfJ?7r!i5{ zaMM^u^;HT5a|A8kin#R%LD+7?3Qep(c;#>sL&aF#SPV_7WS`5lVuiTk98J?ZSAX2) zUy?<-T3tA}xn8#}37*l01%K(Odl9k2BhuGqI`QvY^8kK#m0>F+;seA29ba)O*~4GH zN#nzb2)BS#O$(+FR%RjM3G?m@08+x%A3>sPQH=P)5}Sk zU5Loi(3uuv}4E9Gxg07@#N4oc7eW0?cqi!C?Dlz%@`20?!uk5ntQI&R#`tRb!$u6>8B^&ZsPRy z=x?rM|8q3ev)YM`Jb!_sS?FEAcpi%d&OPb10F;ZBIet^C&=XAyjx7s9;}vzb3mGY)vtbpq;- zDArq1$0du3O%WHORy|(Rh*N=(vs2}VLY~SuwYozq?vV>?>@89U_1isBHTDQJrRp~k z`4EH4?D!p3*i9DyCbqr*o7iSeRXTZ3tlFL6WyvOwWttdTm${jaQ*B0MGC2Nb>U7Qj z#Iv>jq~#&L(L_&dOndKhJLfZ1%csD@kLirs4iK*r_wwA5p-}oDE^?hg*qPyD+b8`r zu$s?0Ri(a_>nLEA|dT4_k*zR$5 zMs`0^IdnuYt&5kUKtiWP&guOSl1=-*or*x@J4&O|$gs4E3{65=hb+15w?lt^ynpo# zhY-ybNWpg9P$eu|@N6+aWY~XEY;WqOd(T(Vn|l(ybCL6@3pdw$ZGKJ+pS;P4*t^?* zvWSHS3pZNpj;OraVrwI~q{nhnUuj%<0Kk?+#lA)TP;fXxbdA%4j~~Jg2^X-wGSO z`Ny1$dSaI5aXLw4Wd$wjDsFiyJ884R$x^6kT)VNiqa8`dGv{k^G5JHQABTf&a?|p7 zyOazLvPpgZ6q{cPEdKZ_c;ACOE;`h*-`Xg8Eyv(2(=Yw{up@p|`S0be0wQ#DX{wX|0(putj+x|AMQPZLp9jTdCB!s%|QbqQ^W$&?za%Uemi!Cs`o_kf00 zzJbkY;L^F@at5R~>FX{JFo=&;KZ$ zdxKl0zPfhW=gn0s)_`+Jnf@Kp*-Yal*Y%$~qffbU{o7O+nIqR5P`USN#idIDMf9|4 zQ5CjC@}2C?zf^w}uWQKpG99N=wbVCHn>NNDqUK*+pc6o;DXJvp5s?xO4p-Pck1!$L{HmI6c@8`wUvms({FzbbwnD^O@ofXFjzt_8a#a);agck)NdSSbBY)+pq>;BV|fQgsoFub$+E3 zS0OHLT;lZ&M()Vo$C=J^O(%c#gzb07(@6ylyv5kLhi;&bxT*5{GE(nS>|W_*t=5Ab zgI`1i7i8Qve$+1VO70G5tsEDCM@Ccatd&NEDn_--I1^Xt3I`Y&s*X3prL<2Jyw#)e zG+*Tu^hzG(8{UiJY&K>+s7PwWJ7>oG>CGqgr_b;!wxO7B9W2vnrFaTlsagnuj0q^TxEMUKv?nGEicW z2pJ6;Q8`De_@*Rcy$h984^io%(N|ry6+TqNbszsGKzdWe-juTpjSwHm?@jL|L|fT0bw|Ch2a$j30Kp_0IesnH%BdoxNdCpu9wwdz-q6nHTh87%A`$=LjSLiTq>Mq=TmbeC8a4V&56W^ z6cvM-%@s;LWhh5G_*mPs-I=3C~WE)>_Tu+H~PL7F3diz7^+!L-WnSb{f=*-A11$+Rn=?nS`TIJptS`P38otYM9)QDk}SrJeokB6|6=m( z)|D{Ey+#l7rcR>$rwQ;qdp})|KBvUsusPx03-_D4T*T|)Xw}vYPM36_xGKaSYI%{_ zb5Aj{V^FBF*xB=9@vYPIA!G!%y~r15?$KzIxa>YZYZm3CG4n5Z0~dO(%ryJn0MofM z5n7)ra@*9!k>Ic;Q7l+PTE8Ku;I;a5NjS+fy4I`bp0CKXYGZ#@X_tOZ{4Vm5)XTza z%#mIj+yXsMjXei-9NIH}OuOD;6e4QjCrsb#!8e1U)mgj1x zbtAclYby*NHcq|t4Mmp5>aT>Ajk?aPi>#fGi~uBKDkq9mrj$|I-sp9-$4dMb+~|X@ zR#T;EQ{eC5xKA9EKIJ{`B>Yi31xnDWZVI|S6Jz0V$D8pK^#BeG#L`1d$X)3%xF;dW zuUndO<1uG3Af44o)B`0J?S_*I$-jTR)5{;nWd964*DJ08J60*|8`&08#glFywttj{ zBAPrwL?AkIA%lF8IT6xnQ_&sFAyLolBy5#;gXZBwl&` zGv2%Y^9>69JHBKM#(AweOMwKFl4f(caNzCTc1(Xng(V4AoeFPh0EOa?55IPRKi=Ta zhy|I<7Eb$mO*QPiH-p0nzbe$a$hD2#!0Qy)yCQV~sWw?|Rkj8}U~lWcP$9Q|M-r=)#so`aBX31rk7`;CPA8OJS$iG*72)5uN{&45dP9bv zTv3FdwFs+s@F%jcks}LBDd0J>rweYPNxD~w%@b<58k(Nx6e;7cC3Wkh3+>747vkB{~WOwZ3f?RkD%^iA?}Hmk#SApR9P zL8&z9LTyrZ6cJ)c7cBRkSl+U|=y+JsJJRGR`nQfzNd469Zf$p5G$sdo%*8cYaOm!8deh182g1MTwC=nkX+g#94>!`#dkY+!YHwVlxkh;=5mN-SFHE zmpXnQD|VNxebRhF94?C)OzO-5)sXd77F#ZT1Ids zn`#m^8|B4mx7ciy#mz5Iiq6UK-(@lgMy?Ss`?l&aN1CN-o@bNk=ee=PxZy#~Zf`~R z&f}k{&a(cc>kl+buGu{Z-*5A3p+p+dBPqx$Hy!uNsmaj$n!cZDZ93G04z)tl9TrO( zy0~KYstwgeXqa=nE##T{x;<6jSZN851*UX+^`B#FR7WU%j3OGiXiJ11$c-an<~~qv zbqluSdWS@f9Z0NW@us#Z!L&&C-^th-@=BrT5;Jv-?-`dI_o-}X@;Rx0OLlzjQOf0# zhpaczg$?b0a&x7p*56a(Dg}&RmF~>9)M4U@IzpffBWQedQsSQ_*^$KID2c68^Zj76 za#aoCU*`qf`mtFu^YWCU(B`i(I=x=S58YmlLQmYU1QqUDJk(|;!+m;L@8vJA*x1%< zVHivvE`_nH`4LJyc74+}eHZ{_t`CYYUPTK}>3@EHW!2u^p@AnEi>(api{l+Rjzqm0 z@l{iOVZ)|{^>1@|L;R`k8MI7CQwwFB6P!LgP6MWvy{u_S7a#Q3kQ z)hZKLWlCFnZr+gK^80pI_&u0BR<1t^1rL5w(D8*T*_?kV_=Rn9qv;QN9RX75^pvg^HmvD zIhO)O_9ZjfnG2Llx%GfXZdEm%sA0@27}|-eY~htru1TuXG}|j?T6|?y*Aq2ViEPZy z9H~)^<*iRIrnlC1-+$;C^-jpUbM|sO&~PD2UE{oIZceaSz>ET3xR%jx+3{VPgS1(; zRTCO3vmu`zKeOExK|jf)kaTA%a#4sk*d4xJIU?%}P?oA#`PI>-Tc^gW?SHbR8Hq0L zGCmz&v$xN4_U=TpZPiVm+yR{4>i{IuE6RgFLyZQVoh-0R9YlJpO^h09Dl*$SDVma! zBeyjxUF&d^q-w*bRzE4GlszyDwe%9c#P@^t!5vC0RDdE*=dVV2wPCaQs0f;5F`gF5 zWS`2Ks`OeOY5p;1|GgnarT}fP5%ch`)^*RDzCI~NfaW;_F?Cz1YK{)+$+TX-{ahTb zW!h?`QYY}TZoAdtIc%b*_OR{99FVA2pwu4s(Mq*0WJU>Taf~m%8B0Nn4Jbdlphpc0k^R!I_XsbuH~EpJLcllO(mx#CM?kOu780L zEr&&4O5`I#EIRZf?C#Ro-{;WG%b$sT$$XSXlND$?>YF&~Gz!EE$dQwWT|%johOBpn z$lasU9g2z@boH3mT?c7U_rr(aGPb)LHvPM6b?aSlt}M_`?>{+S(Q87(MPyb)k99jy z)|BnUX_`=hJ27&c}{lLgM@tn-BB6dY!VgIKRNZEvaX zwv&EnH{OuZ0}0_C*Kb5J{UH%%f-{KJNrP=G6`|g6$3i}KK)Y_x{K~EbgG)v#aO=#tm1W&E~y>A`Fnn{g~Ai?gm&qP zang@oWF|yAGo&mAXO#&XoGW!?&iUO|n1Hz>2JT=AY{;U2k>St=UxoM_+hht`LeFl9W z7E~RiIgTloxg8Em#Hrk(!&;TK?oBN)9l%@@#^5_2>jsxPWG_oJ@`k3!I8K99>&PjofGPM7$rMA3fgbILM8W0JvMhf9%-!IJ zzktYsE1c0Un}n7hxqXP5u*iEf5BHx~!oH+g+ddO8Qf%%9!ES?%X5`3ZiA!cdcVYyF zb5+?qh4=-fDUp-aqD5E_Hq+Yrb5tsKDf+~SXQJG0#ti(yTqb6p@CBU0qss>VLaFe@ zNfyBu*Pia<8gL;Ws$|c%wJGGOFP0=QCrhf|3a2kf(*}begXhKJNQIQ~YR8}KU=>{d z2KMc9o!}spwyHJ_I`PD$BxO9@TT|w0z7TlrWQHs#`}D$(`jy^i6}90& z_fVi%Re3K~8nYi{`52UAoWKTa(yc=Wo_pT8(&BWU64_nBrJ`iXiiMt&pMRgP;66N* z-xXlc3pkp+CxKa<7!7X{L3fVio!ck%qU>=2DJ2WOYNpC9Bc@tK)xww|Vu>94}y`rVYh z$Vw{kXRJP9=Ws2vf3>c`@_YBgPGMnvDKmX8fJex{Jc9i=Z&&7t0GACB+V(u}Dcpc~`@*fI zcR~*I#v`#AaBNTyc4@J9Z<`ZES07#Ro{asj^@R$rT;K+#IrR;h2OHZ()NJ-U2o1?` zv{>v(88nn&@P2H?FjAxQmd|%3Cu>M(rgN4KHbQ=t34y%ZfY{!R3cLgVzy9wO2*Ao6 zS2Zde9T9N8$3GAp_2?E)8s}f9^-y))2(j4g2 z_eUdFPOHDz-*wL9V*{wKI*A4)8>s5qmT7nfAAu!s_gbS0mTW;s-UO%ozyv*eo_?T;j?v?yn@Okw`u;$=OsY`^_9{wJ6u84DOAYc{&Rq#Rn)37 zM?UP2nmO8C&Ek&(&R(!#&=|g9_gKN#3cK4B!W^k~0+SMgJ9{!WTE3HWeUrvA>(u~2 znO2x7aQ}U+(Hr_2z z)fI05ifyO{s3KPlgG~ROrf&{&2OUJwN_*6YN6a2U-z2%t{T)-VWrI8NrFg-Y`-a>< z;C$2VyhsjBN{rmA{pVt9 z$JOVqj#y?*?ZTZLofz$nLqP@gi!)pDUxy5xX>akNw0Tsp|?t_{5XYIjK8h zzstYUuazq+@Rv2ya#JF261Ce8>IOrFWQKuj2U$Yq!H1QU6y5C^6Dd9$!Rm9Ys8umR zLm-hlIbAYNo%7a6V${%q>&L3-7AcxLzc|zp7NY&98pwOI@{*L{FY_RUpWW_@FL-5) z1Ij|opCDupFY<{ZYse1BVZ-17aqj?P;hX;;MZ-Kh9}eC)BSh9y?ste%jots z#Tb%4?wPTR@N$h8Y-um;8~(F;4*C?CIzio=<9z2WQ({%o6_~v(5(-gGxkfew@m8g% z+7V8tfPD?ON{mOARfZt~HCFQ-bLt4n^lRtLyTMX8F)Z*}T7RUwjlW*?2J?(t=aiTj zi_dj(NVn`bG=?fMRcTbIho&=mlOs=-82w=xiqRa2j)0uN#azTeY>T7?!wVn0p(PkUb;Pi5P+jmWH0k|B0%B@`J$M8YQXJX4u6 zX3QKKB-6Gt&m!}XIb>*%naGe?B$*>3%;0tH`!?E$YV9WhQ&+CB+Kp zi?|xd*|NBdr|hmw5AC6YMh(#;LbDgE=PPgU%=s^9nviSMD+X`E1q=eSAv$*gndS44;5Y%;fnJ)M3E z!IbKO`$B5(d7%_57WA#ckNd1VO9qTr%CvGSijRYt+Y}paLx`;sHfQC_HXe~DY@Xfh zo9!UMX{``lrNJT=S`c3Psj%0oe~Vzd3m5ja;Ogjw67#l*#CuDed!L++yRSuRoAuYY zMg}bH1=$p}e=DA~$}NvGcx}vq&64PqSOH?BcB7tc)lA09B=epQm?K{-fQ1yNcqH~| z_gbS+AFu)ub2G>}aR4?}9+&zN53PttEq+rF*J&JLimll*NWqj7m$==`4}|P*)ab3%l|oE3e0a zP4~_V8ngPFho+P(Mzf2t&{;Be{{BZFExIQu*HbQO)T{Y%_@$3ZOal!_WsXT(-&Wv7 z2Q;E)i&=SkK5x@wrTN@c=_jRUP@1ksCARvpjYCF)S|TfKnaB3XPy4mX@EgZD8us!d zTP2b{uEki*ZkSJz;Mz}EbwRetHFOHO8s% z@E#PYG9J3xdFFY-3NV42kxl@tGQnB-kG7#H&4tVa$ESHE9U=v;wihh$K3YYSU7oyZ zL0S1)w0z1K@i#{h5M1lPSg-kgoE+$g|J|SA^V_C7bYoD{vGap5$D1+F^%9tIZqKuL#kZ8Y zS@^wi^K@}<*C~Uj(T3-Bi(KJtsF1Yj{(i2Qeq3dilr$JE@cj|Q zmV0Od1loT)F!1}kt|N?=V@328ElovO4%$O!L_VXGP^%bYmuk*S zkEG4fjz#H;C5u$Df?)*z!;4Sy_K)B5SRU4eQi6ytepBl|B9arjx)a$ z=hSJ6*X#ixXu8u_xwYBi*9b#3=_vKvsysA1DUa=wTO_lhpPR)=l~1%lRR%JhM? zdIq^f$8~Rp*n7PTtl*LWjLoT4{vwfKMSWTDu5{Jcb$KjT<)NRv$?#Tr`@yk4Y{Zb( zN`l1;m#fp;jZhU&UQA3{VAf$TZ%z{En00R{&B`NN?<61RjiC z{FwS6m|j5MjNR6x`wT|Ut;k)|-_$AO8iyp7qL~4I;2I3@@jnOHmp5i9s1|J-5lMlM z#t#jpc6$76k8^yYo<$xe&$dIe%%uq46{kPvzi;T)}7l$?Evla8V9` z>=JlU_RJ)YeUQ;{wM(`YFk>^=_*uE+yirX?Mz9l$KhRq@f#GmNNm^$fD-uCfYJBbK zGcYwvOGTo-pAY}%k#QZz_`uKlb7w$c_sphD)Vy-}Il*rT&%_|`E1Ewd;Rc(!Rg(BL z8VdZL0kBz+ZPIiL>4pLgA2P)})p#P*b&Ng22qa60ODt)bSHxx5n7;a3i` z#Qbd^FjaSLZU7U)3`CF|8&BVB0_tb7PAF=oo6m;xGIAPTToxD~&}|>AJwYO7bgiN@ zdW?Q8J|d)NYR-%eAGaCw^diH~TmN^hNSUP5w>DMgWN>X|*?G&^`OD|Cm$}8ed>&Js z&f&$z$Y*92-FkMU)S**s9c>Qb&KViQGTW6kCP#dno6(OpM8xOft8oG(ITM_S#LU^* z`5-A!mHh>o>iWR>yojh?c-78YoT>5^ z)Pzg@(iz%+!4QaLU;!cMFCMhYrSp{SmnQ02=zbC8Fk%U=;_SenhSgyl)T2LT^!T1S zyKz$C=7(gY{jk@Z{^zgd3)0xqbz*%I9puDwf)meGtE8M8TxImfg`ZOu9y@|9GP5Fb zy3H1g$%!#LQil4Yo9&fDlP)eDMS8hazFY4}z#uI&jQ0R_=uxWvtU+4-q4N+MJYIV~-vlvn z|1J6-^LggB#$*qfxKoFwTkYj`;!IgxYTUFOH+zOO`c&+>mLb6G&nUSF(pMwPOwwlP zXFwJi{9vdr^|XF=aAHpEI+jZ?gWeBqvXJ1^p}NOrShv|*_%;2RlA3JB_%-)D&W6X* za{Wm9uJZfypmgYvIeXjY>BH^Yed+?{*2Aq6#eO0BJ(#aK6cf4D(0 z3Mb!cLH=h-wIzyb&>%jwF!*${F~K!BuT*WzkW^}!d?D*G!M?>)>*m}J6_(M`HKVzm z2^=G0vQW@Q>gkSOFv(#@QClAg7Z`T_q&Q?r60_YE=7Wao!pS_<_ZioHs}Y!K5#TD< zMi-N+xnr`7U5Ck3cWH}=EIqTox3X+*K zA$vu^UJSrPGbn&;3D{)8&|f9s(vd+d(dg$to$bU|F6KQ<`qS{OJAVGdCao&K8|_&H1FmrLHg};06kkkhwWh&sAI{?6)29m{Fq2| zW_JGhuHOR4rzz(Z4mE1*xyh(mh$~vqqIQ*1G$Fh9OA9*WsnJqD(6%I+2kwxRu$8B^{$9YTg(KU-LpJLpf$dtz0F+9B+e7bnlNZ_P>r#C@F-HpNd&bp>L_Y z_WGLs#^AMBuiXfGgXffqk!Z~0ak;^xIs4Wi!Aq#9e+*r8n7X>M7787#Ec%gXS~0Mp zG8Fh}#sWlgdF}}S6mN2q;Wp=+&Lw*lr^atVwN%~K$dZ-C6L@Vm4ofLJ1UgEGi^89$ zrE^`E4Y0k|rsXyp{HS%CSvQ$ywzPQHIe)5Kw_R_Xmyd6Vz-)h*NN)q1+Q)m zxeVNh;!>4$#{14w=CV-Mua)wI>xZVcVXvLy3vr|CQmGwWyMV`A3Q^Kjqz^T2HnU@9 zU9k>*FPS1Gn8|s)H&+F%1xRsGes5=QB0K44*gt0|V@q1lvBqw$pBL-kNk5b6w~`ES zR<3>B;ClYc`y5B}khvjjz9sdX33Kb&>4dFUcfVZ2pSOjqrY%K~efiH14E9+iEJduG zf?aRSZ*39`<8Kzcns|Dh{rxh_Wx?x(F4t?)+gDm|+Rrn((HR+;teMw1NnfLjl9hOB+fw5`(321aPl5v6R zphOW4LbJPE#ICjtk66VJ>eJeuuf{w@5O3Y~6t~%Uy`O)ZMa|?@=O}0JSoRg1)~i(b zsh?HLA@`9p2GL-&H9CkA9BwJ%a2x*VaD_hkWG4gEB}+8R3D6mGu0^g%P-~e?GA1l# zb@qkmKOQk~h`J4(B~M7OYz55U+I}k>fw0ANuNibvdNKKI+;ri3G8!z~PxNFFFafJ+ z0|ncKPnpUK^G)fK*PeUJ-1F)kZwWRna%dPOLjb*Vul~85=7Zd8e892H0>Vk*DfTUM zQ9W8y8hlTrK^J4SA34KJFr%;P09h93- zddl?;g72*Q6QUEdj-S>xH3d`+!YJJAIXs zvi&1wyw{wt{Lw=dQQJB16(fd;CF7LAbCB0v9-Qg7Dw9IO7jiM9QlHv|`QM32-PulC z*(IO92hh@x7$W|{9L_X~#Bi?i{t3_9GR1D~1GoK<4fX!lKR1-Z79|mSZa@@R0E+gH zrm}p6kbs;7*HD#!(#;z!uK|!G*F<^_uxZA*e_B^${upi2eaVw`b*~Z!E;r)-e!RbF zQMe?QOUr&}lmu5!rd28q7-Z2uV;+XB7X%t9mk*v7C_buo-F*6sH-f`|>pv^Wd)4;F z2rqok`~UhqTQ9HlCkba*fc+`u?v{*-4S;W~wWLHw$@9%n>T7be_gzNR6?x&^OzOanJ{)TbXmZkah zYoTdD&bOU6@_Y-|Re~NMEY#I)RH2P&;JXqt#dDbQyg7!k%LqT|zXYDet2eM*L7A2o ztjO!@oj^asqh4$lU}!#<;sp!UI$d&&>nvul8L;y|H+19+V`EgHR1I=L0Bzm~phKER z&4Kz+)H^Csg+dBJ^wlqS2vd54>g@)ALEVi4ZjAWTE?c z4**0eoBtUTaNek^l~{SqV28CXtkG)bztfGL=}HSIGR@io0mCLp<(mQ7f(#u%LHB&O zr~h`C*x7dhN1`GBq+13lgs6%LA~;R;2+{HQ=v$8mn$*OFf72^s+qB2J%aLSH;y>3z zE6T^)H@$-;$+=q&T&?o}?2SPQ6TSrn3AJ@Un{&wJ+B_#}%L(4%=>y<4@-pZy%AV7n zBgIv==#PFt$S?opoNwtaJSM-6o^D3^1Z4 z=|C82a|^(xLWv&$q2Z1zUr{X|^{__hIysrGbLTTQM2jC;B)UXdZ6$dwW}DH9LXF%r z%H+9U;Db1TM;wI^RJ@tWej@P+rKsvb*;>8Cj-KZyR=9yp{(vrNC5^ryI1~ttX zyI<^}lH}`LB_@{HSarRMx-KYL(_m#imYmNIC zs_g6+88+J3Lqs@e(U4X4 zs0<6bGd6tQGz*j0%6VpfT7EcoH&EPl*rAdBUVXnd3e$Om_RHelAyTQbT zeVX-jd&1UPF5ar`qUV6+*88WeEL6bJawGF%V%~_30M?-Tod`#KR;KT@6!u}LG|E-{ zz!Rr!Tv1{9#rK-5f2iCfOj!D|c=9K@_0CzefY=y6#cEBdBtacR=5Tfw&1`dn;y zDb-F21KekFP?(90&}Eixp-1nG-&;@SuH3l2HgUhPQFpIBJxMMW&CsIO`C?&hkp92H z7XM?fM=<%zNhdX15Hh_-8z2znLd$8kom&2iScIHl@bMgy5IL zu~%1Kl0%A-Idjxjnh)D`zO>6{)md*_pV7vJfnG*Yg4*Tzl#%=WTE3706*nZ>&^(G> zySHbkhBx)RusCMv5iMP5Qzjg3$+N`6La>a&e=ei+j_YRACCA1K_t>$F;#N9mQ*4N^ z!4~LG{-!m-Ujdlu>FM8?-eku@VPEcAo*q-a^&L%wU6uOL6(QoC}G2u61 z>jTzV;47rof-R~c+NA=#@ju?2dsfJlZ5*+B0=O=LaGhtTI#C%!&EJ9b zOfOHuY|I^a|5M5>Q<*LLCGz7w~X*d{^{Y z`~{+3XZgYtTZz2cITtt?7n3 zn1pPisKUB{&&}xggrf>Nr}g#eBBd<|FRrV){H$$xnh@bNNT(DE>0Clyy6h+>4sS|; z#s9RiyDGjv`gTe!%y0Ggf4uKvg_{=d1r;qCwBh@E-7>SOO-5>T35Ry5$9{OI=XG6Zs#%P4faA)syaO9i?Bu zSRsSs*U6LCCZGKPzQ~=>;SUkX_8{*8@d&b8&I?*rT(A@ukz4&7No8 z>iF7V-O2#kyYm=|5;i+~{N_-^uDUMDrpc5cxiXw4srsb`)wxsr8D!l{?mJYoVf7Yx330jXLnlE^B=j zTJ5oXvXOyy$w`^d49fDCH0@KF44+-YI=@(nUf7`jF+577&nXXC3O47h+27}Td-@hC zBEO^skn`d+;|KLL7^Cx<^U_Z9IV!X-nU&P_&aWYu$YvlvC2lh&<-6)tAFjyV-kT&r?fL$!b?taq#+JUCmk-1sO?Ld~Aw2Qc_b2P6yV zFcZG!3HR0@gRZNBM6$)u2D=rY%yS6w;{C6ZJ88p+Tqh6%OY^`f^Eymif8*DcL7QPL z2KV`#B{+4AL2F1;jtuB8+0TDXK}7H?s)UI703?b2$~W2W%7T|YbOy8OMz+10ecuM- z)RDz}V;~p75W1D;H7U_$PazTc8o>`%0K1mjIWAdDBndL)U1eMuwL5@(DEMrCO6Kql zHiq`hID|xnB%WM(NG-i*UPONd^{fcwPzL}pjvfsF&`n`TTc42lPzc%&hPz{XTlW|0 zPt;{7B#YM^ZfV0*d8=;zIrF&f9xjFfQG6hK3fYMU!<@KPx7^zFdrnT@qs%;LSzmFw9gRiK;+&c z*Ty;$M5y>YQxpu3x2uw?UAr(J!$sXmWp(}&RG6dy#vRpfeBaRLcdKy=pu?IiUf~_r^Ugb zi&WUK5B+1rp3nts*fAw$v<91U)sDM~37o+G1fanPdr1_yT`!cA#mj8;yPexWttcmW z7(Df08?y|pFR$r5qI>QRNWU2fnrPpBc*4PXFblZXJ4^TL49&Ukv{-pr&4D66N_6qn z?fB|$>oa>lllPiZBp;kOHR}?|PNK7gz#<5|mjHqjwl;eJxlJKefFQ#ux2jF1jrxs{ zTYY|&BxeVI3p|(f`RwHH=(=rbBqb$qe+$5;2p~N7ECQsv6x7x48@*&$wD$S{V&-0q zzRqKb^~;O?JzkLSF72hWAh`V{RoR|S+$u&-w6(Qx|86RG)yMuYZo?;J${^anG0;S7 ze5IA!Sig@bNe6U}hE5d-6Oc09C57g^>znV6TzxIrtD(IK5QB!W*ZuqBe#9s4p&Sk^ z)^karLvei=2q^?Gx8ymCaS#p#%-8Mh=5YF(9>OBoLz}W*trpT(&gHf*BDK_Ft3ZdK z6;d>xE?x_uWXSMU=w#l7{AKp1x!j)w7gqu(| zbPjJg{YHfc@PenfX#aueg;uG?#EVoX6DQaZm+_-qU2K!#mwygIn0E~i% ze-}~?edZ$H_I8){a^_iGMc+<;)?I_m`3c<_GUr*!}I>x^|UQ)b|n^9`FF6V2#=2xX8?ZYbUeOgWt~6;dLKI zU$Qf}`-Z3(i1IB!kic2JD=l?sa6*6L=zINo`I`b^%ePzKG$2+4md`V(cz*pO=;e`< zqLoy1yw`sXJ1LhrP>xg3z1J-F(?bhT@BSi8W`-A%!A}3m(@9C5uThZ^L6evWdk_zvBtUSb>bmU8U8x zdROaJ3yo^5)YUItI2r%p{ig10Zcz2OPOR}8T<4)5yBCFoJl^ z;QI|=DNiC;WPT6mujUfqHR`|bUr0U58-k?HkPbhyv;koMg}-jRg-lkukML(`2|Cp7 z-FNyS39cz(g5>Bh?e5i?V*KXxwUnwbkLe{~!~Yj|d8h7`h>18k=IvoF$$DCbZQo6V z*TqeCYU#&q4j;yl!hvRb<&aQZ%i!6$f%7~79W2nBEMy#DZ}C-UqY2g|R)83S0~U+2 z8N;RAVOO{kx;+_w%+d>dY%0ZK zf}?J?{Y{7cx*&n1^f5&UDCimCbjdAbU&h=fDQQS_m;=w}f6gOj=s*nxw7Xv;kn?`f zRB%f>3+|XKm&k)0btP7k_qHtE;_&nS8NqU^6z;L>+$ z1ianKd;JG^8}4O7hJd%t-|x+pqUa0>Qs;PC>4FN)QQ1OIbis4Y70mHb>Ru+SBQdGU z9hI**hM9Hd|0Z<1|1*=AM^A^-^nHT3ic4^#mzpUMrOHjI5q^wMp@uq=0RC9fW11AbOC(DC`qukRGHZ6^{ z(WqO=X#!KHOHLeLki=12bE;yTtza2V>&S%cVyc|?bA0nhCg!Za&f&AO{Pt&Fo3Q3s zqvoM>6`6bz`~_(}0BImah8r$hI!Vzd(JqEOPsE%3P_fi#f%GrIQu7^E1LpB%cktmFuAWJ_A72qEvL3^^giRI`Q$n^YqqHA@hp9tBbPx2o`gH{~@`ymHNss=<2NT zNorDdtU+i9U)w~>q}zw(vLU|3OJm5L+X@bKjm;IMQ^lK2I1_pc4B2Fk3gfa0>4?S} zp(a44Od||=V^zizp{FBV$Gq>+UG;elauQ>z`P*ZAJIRBZr13Q0*B)hE1#x(HGV@yp_XE9XBYu0%)@A064$2=vKGS`kB_{5}e}me$NFv@m7W!PKS<(z@ zC8@j2*r!WF|D23C>z2ALujI}3BDJGPuCMLXXqGr{l-$sXjKgwOoUmd23gI$ru+E8g0Z}#~+=b z?MP+hG4T}{JErY?h&iE-iYf4Z#o;HnKJh2W#{OcITg8_kn#`;G7MG*!Z+aMeoo1UJ z@7$Mue8v+`6`gpbX;L0XxH!|FZDIYifdtWZ)eC~sYrcF5L=Wy1ex^L4d)XV&KFVS2 z_<4;PbDEWp^z&QyAVBHuNpP0LvEnww6qrN4w~PdQbd?C?B8Wep*$OaDgl=#?G;ZH% zb+y~tWyS>tH*0idikUDnTOS4)@@O7KHFey&>0rQ+e@v3MVTemg)%cLnVRb&uWm4RR z>U6j)lj>EOnw8ycgX)BHSx2O{k>RA7_u$@sA2+1<=&ZzGP@FpX{*^p| zaWH*A21PeTf)1-c_Ugw7Z6>Slx#we*_(>p*7lJT*IL<>rtAGAt{kl`0ud`Iy! zOo+U1a&V*^8TKW=a(D1fW_F^n>evaiK#8yY%l8-KhR8NN_$L+y&aewJ&~U>Gb)yWOr%|sUq-fS88$#^^F0lv&s?3c&Gs6 zmi+dHRofZ~!{}h8ccIKXd{DnJ_)Y!EK1x>>$SIOY!(amyT{?5Y#`q35TYkS93y##( z)-TH%>c~$%xQ+{F3>@a@^5~*4)55-|chAed=b&YpX2Oc8az9~sns)L7eR=mjsl(rt zuk-8A$_~u8Pac=^p&}QcGrAa$1=GHg`lnQr-&b{3PmegWGc{R^y0@uC(0%qa?K0PT zeifEcb>a?o7-AV~o+mQ{c-Opxsp^lZEAT9sKA;p-v3^)2h^OA|^2P}_nOaBhr>CwiM5Jn0(@uIjMx*bE0xS1VP{hSjDDT*AX0r>@Lw9`G?Tf|5~VCI5L zY$;7#WMT2-Bpr>vX^ExfNGGJIq|X;fQtLLcpR!WvakZ2fhHLZ*o(ZQ!m*7EpD5{vY zGTNo7znHiWfo$F*x{yL(i&ThEqlbtlF0%0IUmtDdyPn&2Ty-QSJXl&a_p*3rK%?vs z+_5!B_K}p?ij&06ZsnqS>P!A74W%A;rd@r16kSKytRkd*-ug;r+#tO!YqXh_wQ48R zCa8m8$_BLA4+1XhV|cz-4i%z~z1f@gC?iuo3S6K{ag4Np5+FjQfI}@9#ym)W{N963 zE|1}zoh4lF|yAT9uhf+{TL3rL~kCuOWHHqM;&AFS;X#CSfufAmC7?%e| zG>4>`BrBfUyU8Qhzn;9DE=CWV*=05Gy9HQ$i)6a|I9Yw1nJsNexTL>PvGP;dTM;4BdzBY>h-o z(EDh#{rk5sB*+U=@?()hyx3V=)KI%dekZc(6(@zGX z?$BJ!Qy_9g7<*YnP-crNQzmbg`9>{a!%quOsch2Ql`-@;eb)==N*4F)IqgFzOm97UcCi4|1Iritq7ZYZ~}P;(T~Qkn;_&20BZXn zpr1x?5#jh);RA??9Si|+35xGeOQ#snVJE_uS&Gd@he)*F8$TL(Oo_4beV%!4jMgVhQ{2Hl`v9<)1BCykaTCi=gfV9|JnYyoxI%=( z!_4EAr9M&EAUn%>*0$3A963e?Uu|s1n{Uas(gwW<7OSn%16zsX^nRB4*Wu#u+cKfy z0LR@}60hMC4YWq{-DrHOf!fa{a=^`lOXl89f#rcmEGy6DB%{tp5~$neU!X^);gJr} zdJ{dXWU{j2RO9SZK7^CQBW>3cU>G1n4Lt3 ztFd9D`@xQuK4kGy$^1>r4n4XTErV}ORccrNaqzOltW}!~J(-U4rQ_H$dV;!I zR#kis6(b}s$U%Vg!Hrqp0e`a`Sis%ejU+3v=?<_qrx&)CfQ<#V!Y)(oj?ujlaLb`IJAd|Frl>JwdW&$XFl;_VKGK;ue&GS zmmRnwPF`fl5TDSK_@l1TtGtbu<&bBX23>RWw6fq~c_rKYU18k$5 zDSp~A5rzS`o=Ac|w!>mp>=7vr3MZC>OjIi0t&V`)06{Z z@L}o_5|~dmZE#^vP`WGislO{Zu(TM^%qYQCRtb4do<$8%u(D)_n7mSOtO>t#)Lbj^reQ3AqRFJT=QGs6><{arEGu5AI$K zA<^)Ajz}BCMpE!xwvphaim*@?(FbmkkMF(MWH|`&w-=vWnEq?L?wc9%w(EMHB^)D4 z3r62+ge!3C4e?gU${v-(g<}w=2st`UBGAyN@MQRav&LnzS9o&JAD#9>l#-#yBPS;! zoe6N_<9(l{ss6&-AKc&=fanNkXB!(2oLLr3xx$lW89w%L1~gwx8;3OeI5>=#LZ#w> z)2Gfzc((n;*Hti!S|PxJTu_#Bel*L5LXzwOqvW`Krw(& zfBH67@%dJe-~tcp0DaK{Vnj9}8xw}+>b|I0#fp}Gq>k$IpSe|IA5MKF50B*aLGr8# z;67GBkJExri$Y20a#+A=*O*N&_?(6T;K%nOl;=Z&w^$rXUjT>dy&eT+?#x6BmyJk5Gl<;WI%9$=R| z<6Y_@Oo?kc%JTCbs9JEy0q56a?Ovkr_M0O_2n1T8S5`0r4W-a$Lg46o z=Uz=MUFovp_BX6|kb*RTyV()S_B_-S+wz90ViA~Y3beVbKXpJ=bZ$mDfX(#D(*8iIT5DHs+#vIP%z5+5YC%eJ@oweT8Pa9RTBjrhPIg*#+#IBz< z@bJi$lfhY|)hjEDAHmc4F{77#@iYlxXi-Lwkx5lKl(~Clu>5c%lHpm)>=y!&sVl5> z#-h#H`L&}44Zr3Po8s#0oXv;C^;5NlS;SXE+*ekP$dl#^M0i&isFj32)llJ!|pWj(0u%*5H?jHF-m zz1Nj9LCDTQf(`SH&h|^GVbr<`UqeP`Li*F-p|IFD=6cINe*hQl@e(WdNvMG{Eo||% z$Rx0i=lCnInf@dj8jkQB{l5NCeaDM$mg{HA&l)(Q`N`nsh(r zHB4;P4^pH-v%z^^lWMklch8fnXwji3KV3@`O(`h)KtCH3%8#NGW8fWi^%6^WCBt(< z6u7uB2AhnC$$K-hu0S4jPz`?E8Q+7_Mf*6%XwD(etfavZRm~VB$WQl{os=ZqPTIyR2-0=tMxymonXQWS_o1^;~)pR-~%G zl==d!$gs|zKY~ttR*cJlG%|mXfq#0e64#WBA8%~{ZZUyQPH4Ga>#d3}itaJ9e~CX- z7UFU!+*~E)cwj7575cNq>lDt+?p>aj?h=$jF{}{hqI!@GpJxg`zp0hihW z^%_w4owPK8Fi)d1qyZ^lX^7sgnq3SyNLr6v>+J~VDlxVcr6Ji7yE6-{E}#!;ABI^o z`+*~7fGq#>|9rL{8M#G4DJtjL*1{<7bkJSc5{vHL z&3Szea{nO`gNf;Qk~jxRY}=W`IlSXjD9U&R72o9vajrNsR?l;Yv}g_UHTz4u4-Wb9 z8iZ*gkEIa$V5#c?GcY4y9Y_yFmz9-&Jh#LV^ixAXgXKP6J8TtBBi zia}CBz2EGSDN(SH1&fLYdFsKhUwd+TIt5sr#HzU;Vdgr5{^c-Eac9U2T%4Z09B?J0 z9FdK81j2U{C}2;4T=}z9sel&XzJYLy!BTo8@GdOR8A#1sU(pEBKqg#a%Jn1_RB@Y* zQ>~aWK_T4lKW2?S+R3Haj>92@c76l^H0M%3$cH zYgUNVJx@y%bGk#iPcUTPmfEgreU@?nD+>Blr6C5@f5V!8Z)D%)TR9rS5^BoEiNIRh zo>9J!DB{RLx{XG{u42Ts8^n-u8O~z#nxk)+`I}xWhPw0UNYE(X2ptmKB7D zkos-G$%c?dZ)IC=;Al*N^iz(a@xo|LwSQw;sEB+Yv}qR-I?rOjI-}4cNckde(5mRL z5c6E_n3d+}vo?&@RW1D)AlBr6T{r<%bP8v*u66w}6LHbGw&k~r&dY<&YtywPw~0VU zC^fiI=d<$_sWX9|I&7clo6PUYA@UHLogqR)XCyLc`&C2smoJpSznw4@mXAc}p#>p~ z`P`q&$KAiU5k>*7%%;GpSDwFB=GR#Xk9+PgG(x$h)a!>B!KFB}n@7e$$mL%0k1Zjm zf;EqB=R%RT;xfvaAaBiU+H_PGGzQv&h1eT{)BP~#pYpfJP@~jIVx7A7$0JNk7SF8# z%bw~Ooyi9kMuo6L|LrGuIs1=NQ1C|RN~-B=_n3*?tV8QCKfIOaA8!>KZ7rf%gs(E_pI*+pG1+tsOkyJ;{o`QFgl?Q4|2UG~Cv5$EqJ zv6mfgWwg6pF6+HQ6&IOj+i~63=4|A8XYNW88sl+63sN?E8e}*4>q5Eo%w@W-y~gzZ#WNh{|Dfflox%Z#6z{sq zO=2SCfKZv|bwS+3zYYi(Yqr|&=y!jiao@NFJW~j=Ph_>c0(<-)wy7Y^k6ekI?EG*6 zM|VU#frJltQNxtMOnv`h5KPN@GC*mBsTUf#vyulNm^6GV(|6filr%mM)C_)yh^hN| zkZG71uwi&VIE@s|_|6 z^q0+P)Q1ts4PcG~xU;!T>-mD)9_<{&SED`W%8n%MO)bQ1ba(DscR21uQ zG=nHX_b|8n%WxpndmQ-ec(N0@czrKmg{M|F5Ac z!4yrcGUOMOA3Ufx^pjS#sK05h`|uBt>V15z_q^7@7RexhE4d0j$GD7NPH-+rT>TG0 zk09K(_dh9gFtJsE#O2hFVeNRfk0t=(BO0x75aYn*djFHJRQK(tTk*=vNFKN;J^m6f zo(E+bmFWkrN0A5sBlu)}$iWcpCj9arBJPdiDxn1tybasmZzFN=NxOr$c0PW@hK&N= z@8-st%bXBgUCcfRveglc27dEynuO*DP5<2%;}`GPiz@*z?W8(@mMGH+b+*PFm@D!8H^A{{u_?F&+Q_ literal 0 HcmV?d00001 diff --git a/doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-dp+cp.png b/doc/gcdPeripheral/img/murax-gcd-diagrams-gcd-dp+cp.png new file mode 100644 index 0000000000000000000000000000000000000000..68daa3c8c902513a09ee7a368431aa56868dbe1f GIT binary patch literal 27013 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┽ zrSoOLZXh%0qS4Ekztt-%)@u8DdnjE8w)T}aDU`kQb1x+zN(rE-r z0;UNA5!x*Nwi`{$GLWSl%3gWt`c)}O#2yYWUfgLBJ+V^I6dI_5TO6;iL4WTR+GwR# z?*yJDqIffmNjQp@S62h2Qq7wYiDydorQ>;fK4cvBWinA_lpkOEnN3 z)hX1|lme|9X<(Pz?>3|$a-0_`)9!hjf*Ws^+QYwBW~i?ZBYrP?!_S$JC5I4smisbR zZ=PjYUAWh@!9jerkU9tPnYH5KmdM)?Rz-CDUi8Z!C%!PW_}~#`LKn!Rn(~Kbx#{m` zB}%2KdpUrg$@)^%n8Jmavwv@_8Ca|%frz_#;vhg>X(C>;mE65bi{8p>rScgNIv2b> zklEQsd9LDYtvX)CIiO6mxTF)u=UzQJeCVpexALy(B59oI7w>%_f7oD{0W!(%rW}IO z`&dwQ+HAj&{kmNaFn&IF`=Ll&SOr6uUEWX^OtU2H88e?Dr5~q>TI;O(U|gVP+bk9Q z^z4;x5L6Ck`u}3klL(lc6>L!Eual{E80SgwX(}Y-A8j!XLZnxT6lWxpgl%=ZQl$~m zng9=<*KvPGb53!w7DvlEueSf1v~Z`Or9?D$lXJbWOkL`gHSsM zM)AZOWDL4>{w_rFV0$KwDm3&u8ixv&dwc6X`^^<}aZE^1rqprh4{FNJ*(yMPzypPl z7DWSNJot|T@fBEa!^%=_fV3qP69_n}6RFTfUi)LiYG$#_Tm!WnHR}Q$;zIyv z*KB+6>u=G7Vrxt^l-1!noRRD3mHnF>g4`vIF$6A142F5!?E===7zk>@_%A-z>h7pB04ROX(dka-+S>%6>tx*ymSYgObJ z_mneYT$r+kX^C`=+T9*G#Pd#2H%Al7T4P+=!y*}CidY|T4xVu-@cojAf?C!JKG=d# zCp+;8lnUkf11TYrRyh3S01rj49bGm!%cckNWEM z{~Q%CJj{UgfeoGPFU;Xn3Lom9l@)CWFLS;xR1*!ix=dnBt#CPnQVTA@Sd{rYxbp9U zdb`E`g$f1oXCmX%cX=4N8n`ciCztdQHN1CL#}-#Rkxq2&7?f?EJoanKBTNaVF!DEH zPvP2*(BY4;yswL8KWGYHVDPb*rUO9*D>#w~>mMhq zX`vc2`x>a<7-v$GkrG}xd8E%cMv?$%dt)@%LXDTUBbk&-&T%h zRgk={Z2!pLrXers6q*E+0!f=Mc>aHDGQi#qb=zBErt$-+TR^;fsh_00Lh;c7`5nO+ zeWc#-MoJ3rFYCb4C|M7D1@`m#-#2soJN$V;|57%xU|K1&P+B0yZlCbIgosXlDD3K? zWxM&srR0BGIXHouM^#lnT7tEzx}kp-`+b?Yw83tOvmAr)!_2~dGCM99R4Y*f7%9Y!2PeHkAK?6KW*cmw((nf^Z)Bvu)*DZb}IGv z5BjkBppc&`66x?XwXsPZicEgPbrNT$2V;F{d&vJB-PPT`9TI(0rq+I1&ZY-HuXIdN MR!ye(I@b5U05CA3NdN!< literal 0 HcmV?d00001 diff --git a/doc/gcdPeripheral/img/murax-gcd-diagrams-gcd.png b/doc/gcdPeripheral/img/murax-gcd-diagrams-gcd.png new file mode 100644 index 0000000000000000000000000000000000000000..f557a69094311785d8d3241b43832e7fc812018d GIT binary patch literal 15632 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 ij~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 \ No newline at end of file diff --git a/doc/gcdPeripheral/img/simulationWave.PNG b/doc/gcdPeripheral/img/simulationWave.PNG new file mode 100644 index 0000000000000000000000000000000000000000..93853cbd702b2a66bdc64f825b147929d59ef71d GIT binary patch literal 48129 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 literal 0 HcmV?d00001 diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/makefile b/doc/gcdPeripheral/src/main/c/murax/gcd_world/makefile new file mode 100644 index 000000000..0f4abd87d --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/makefile @@ -0,0 +1,134 @@ +PROJ_NAME=gcd_world +DEBUG=no +BENCH=no +MULDIV=no + +SRCS = $(wildcard src/*.c) \ + $(wildcard src/*.cpp) \ + $(wildcard src/*.S) + +OBJDIR = build + +INC = +LIBS = +LIBSINC = -L$(OBJDIR) +LDSCRIPT = ./src/linker.ld + +#include ../../../resources/gcc.mk +# Set it to yes if you are using the sifive precompiled GCC pack +SIFIVE_GCC_PACK ?= no + +ifeq ($(SIFIVE_GCC_PACK),yes) + RISCV_NAME ?= riscv64-unknown-elf + RISCV_PATH ?= /home/sallar/tools/riscv-64-newlib-dist/ +else + RISCV_NAME ?= riscv32-unknown-elf + ifeq ($(MULDIV),yes) + RISCV_PATH ?= /home/sallar/tools/riscv-32-imac-ilp32-newlib-dist/ + else + RISCV_PATH ?= /home/sallar/tools/rv32i-ilp32-dist/ + endif +endif + +MABI=ilp32 +MARCH := rv32i +ifeq ($(MULDIV),yes) + MARCH := $(MARCH)m +endif +ifeq ($(COMPRESSED),yes) + MARCH := $(MARCH)ac +endif + +CFLAGS += -march=$(MARCH) -mabi=$(MABI) -DNDEBUG +LDFLAGS += -march=$(MARCH) -mabi=$(MABI) + + + +#include ../../../resources/subproject.mk + + +ifeq ($(DEBUG),yes) + CFLAGS += -g3 -O0 +endif + +ifeq ($(DEBUG),no) + CFLAGS += -g -Os +endif + +ifeq ($(BENCH),yes) + CFLAGS += -fno-inline +endif + +ifeq ($(SIFIVE_GCC_PACK),yes) + RISCV_CLIB=$(RISCV_PATH)/$(RISCV_NAME)/lib/$(MARCH)/$(MABI)/ +else + RISCV_CLIB=$(RISCV_PATH)/$(RISCV_NAME)/lib/ +endif + + + + + +RISCV_OBJCOPY = $(RISCV_PATH)/bin/$(RISCV_NAME)-objcopy +RISCV_OBJDUMP = $(RISCV_PATH)/bin/$(RISCV_NAME)-objdump +RISCV_CC=$(RISCV_PATH)/bin/$(RISCV_NAME)-gcc + +CFLAGS += -MD -fstrict-volatile-bitfields -fno-strict-aliasing +LDFLAGS += -nostdlib -lgcc -mcmodel=medany -nostartfiles -ffreestanding -Wl,-Bstatic,-T,$(LDSCRIPT),-Map,$(OBJDIR)/$(PROJ_NAME).map,--print-memory-usage +#LDFLAGS += -lgcc -lc -lg -nostdlib -lgcc -msave-restore --strip-debug, + +OBJS := $(SRCS) +OBJS := $(OBJS:.c=.o) +OBJS := $(OBJS:.cpp=.o) +OBJS := $(OBJS:.S=.o) +OBJS := $(OBJS:..=miaou) +OBJS := $(addprefix $(OBJDIR)/,$(OBJS)) + + +all: $(OBJDIR)/$(PROJ_NAME).elf $(OBJDIR)/$(PROJ_NAME).hex $(OBJDIR)/$(PROJ_NAME).asm $(OBJDIR)/$(PROJ_NAME).v + +$(OBJDIR)/%.elf: $(OBJS) | $(OBJDIR) + $(RISCV_CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBSINC) $(LIBS) + +%.hex: %.elf + $(RISCV_OBJCOPY) -O ihex $^ $@ + +%.bin: %.elf + $(RISCV_OBJCOPY) -O binary $^ $@ + +%.v: %.elf + $(RISCV_OBJCOPY) -O verilog $^ $@ + +%.asm: %.elf + $(RISCV_OBJDUMP) -S -d $^ > $@ + +$(OBJDIR)/%.o: %.c + mkdir -p $(dir $@) + $(RISCV_CC) -c $(CFLAGS) $(INC) -o $@ $^ + $(RISCV_CC) -S $(CFLAGS) $(INC) -o $@.disasm $^ + +$(OBJDIR)/%.o: %.cpp + mkdir -p $(dir $@) + $(RISCV_CC) -c $(CFLAGS) $(INC) -o $@ $^ + +$(OBJDIR)/%.o: %.S + mkdir -p $(dir $@) + $(RISCV_CC) -c $(CFLAGS) -o $@ $^ -D__ASSEMBLY__=1 + +$(OBJDIR): + mkdir -p $@ + +.PHONY: clean +clean: + rm -rf $(OBJDIR)/src + rm -f $(OBJDIR)/$(PROJ_NAME).elf + rm -f $(OBJDIR)/$(PROJ_NAME).hex + rm -f $(OBJDIR)/$(PROJ_NAME).map + rm -f $(OBJDIR)/$(PROJ_NAME).v + rm -f $(OBJDIR)/$(PROJ_NAME).asm + find $(OBJDIR) -type f -name '*.o' -print0 | xargs -0 -r rm + find $(OBJDIR) -type f -name '*.d' -print0 | xargs -0 -r rm + +clean-all : clean + +.SECONDARY: $(OBJS) diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/project/build.properties b/doc/gcdPeripheral/src/main/c/murax/gcd_world/project/build.properties new file mode 100644 index 000000000..dbae93bcf --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.4.9 diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/crt.S b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/crt.S new file mode 100644 index 000000000..62d67b9e2 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/crt.S @@ -0,0 +1,98 @@ +.global crtStart +.global main +.global irqCallback + + .section .start_jump,"ax",@progbits +crtStart: + //long jump to allow crtInit to be anywhere + //do it always in 12 bytes + lui x2, %hi(crtInit) + addi x2, x2, %lo(crtInit) + jalr x1,x2 + nop + +.section .text + +.global trap_entry +.align 5 +trap_entry: + sw x1, - 1*4(sp) + sw x5, - 2*4(sp) + sw x6, - 3*4(sp) + sw x7, - 4*4(sp) + sw x10, - 5*4(sp) + sw x11, - 6*4(sp) + sw x12, - 7*4(sp) + sw x13, - 8*4(sp) + sw x14, - 9*4(sp) + sw x15, -10*4(sp) + sw x16, -11*4(sp) + sw x17, -12*4(sp) + sw x28, -13*4(sp) + sw x29, -14*4(sp) + sw x30, -15*4(sp) + sw x31, -16*4(sp) + addi sp,sp,-16*4 + call irqCallback + lw x1 , 15*4(sp) + lw x5, 14*4(sp) + lw x6, 13*4(sp) + lw x7, 12*4(sp) + lw x10, 11*4(sp) + lw x11, 10*4(sp) + lw x12, 9*4(sp) + lw x13, 8*4(sp) + lw x14, 7*4(sp) + lw x15, 6*4(sp) + lw x16, 5*4(sp) + lw x17, 4*4(sp) + lw x28, 3*4(sp) + lw x29, 2*4(sp) + lw x30, 1*4(sp) + lw x31, 0*4(sp) + addi sp,sp,16*4 + mret + .text + + +crtInit: + .option push + .option norelax + la gp, __global_pointer$ + .option pop + la sp, _stack_start + +bss_init: + la a0, _bss_start + la a1, _bss_end +bss_loop: + beq a0,a1,bss_done + sw zero,0(a0) + add a0,a0,4 + j bss_loop +bss_done: + +ctors_init: + la a0, _ctors_start + addi sp,sp,-4 +ctors_loop: + la a1, _ctors_end + beq a0,a1,ctors_done + lw a3,0(a0) + add a0,a0,4 + sw a0,0(sp) + jalr a3 + lw a0,0(sp) + j ctors_loop +ctors_done: + addi sp,sp,4 + + + li a0, 0x880 //880 enable timer + external interrupts + csrw mie,a0 + li a0, 0x1808 //1808 enable interrupts + csrw mstatus,a0 + + call main +infinitLoop: + j infinitLoop diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gcd.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gcd.h new file mode 100644 index 000000000..1d3ccb708 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gcd.h @@ -0,0 +1,13 @@ +#ifndef GCD_H_ +#define GCD_H_ + +typedef struct +{ + volatile uint32_t A; + volatile uint32_t B; + volatile uint32_t RES; + volatile uint32_t READY; + volatile uint32_t VALID; +} Gcd_Reg; + +#endif /* GCD_H_ */ diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gpio.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gpio.h new file mode 100644 index 000000000..34348fec0 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/gpio.h @@ -0,0 +1,15 @@ +#ifndef GPIO_H_ +#define GPIO_H_ + + +typedef struct +{ + volatile uint32_t INPUT; + volatile uint32_t OUTPUT; + volatile uint32_t OUTPUT_ENABLE; +} Gpio_Reg; + + +#endif /* GPIO_H_ */ + + diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/interrupt.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/interrupt.h new file mode 100644 index 000000000..23b7d277a --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/interrupt.h @@ -0,0 +1,17 @@ +#ifndef INTERRUPTCTRL_H_ +#define INTERRUPTCTRL_H_ + +#include + +typedef struct +{ + volatile uint32_t PENDINGS; + volatile uint32_t MASKS; +} InterruptCtrl_Reg; + +static void interruptCtrl_init(InterruptCtrl_Reg* reg){ + reg->MASKS = 0; + reg->PENDINGS = 0xFFFFFFFF; +} + +#endif /* INTERRUPTCTRL_H_ */ diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/linker.ld b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/linker.ld new file mode 100644 index 000000000..57bc2f7bb --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/linker.ld @@ -0,0 +1,110 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. +*/ +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(crtStart) + +MEMORY { + RAM (rwx): ORIGIN = 0x80000000, LENGTH = 2k +} + +_stack_size = DEFINED(_stack_size) ? _stack_size : 256; +_heap_size = DEFINED(_heap_size) ? _heap_size : 0; + +SECTIONS { + + ._vector ORIGIN(RAM): { + *crt.o(.start_jump); + *crt.o(.text); + } > RAM + + ._user_heap (NOLOAD): + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + PROVIDE ( _heap_start = .); + . = . + _heap_size; + . = ALIGN(8); + PROVIDE ( _heap_end = .); + } > RAM + +._stack (NOLOAD): + { + . = ALIGN(16); + PROVIDE (_stack_end = .); + . = . + _stack_size; + . = ALIGN(16); + PROVIDE (_stack_start = .); + } > RAM + + .data : + { + *(.rdata) + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.gnu.linkonce.s.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + /* This is used by the startup in order to initialize the .bss secion */ + _bss_start = .; + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _bss_end = .; + } > RAM + + + .rodata : + { + *(.rdata) + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + } > RAM + + .noinit (NOLOAD) : { + . = ALIGN(4); + *(.noinit .noinit.*) + . = ALIGN(4); + } > RAM + + .memory : { + *(.text); + end = .; + } > RAM + + .ctors : + { + . = ALIGN(4); + _ctors_start = .; + KEEP(*(.init_array*)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(4); + _ctors_end = .; + PROVIDE ( END_OF_SW_IMAGE = . ); + } > RAM + +} diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.c b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.c new file mode 100644 index 000000000..fccbcc2fd --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.c @@ -0,0 +1,62 @@ +//#include "stddefs.h" +#include + +#include "murax.h" + +#include "main.h" + +#define DEBUG 0 + +uint32_t gcd(uint32_t a, uint32_t b){ + GCD->A = a; + GCD->B = b; + GCD->VALID = 0x00000001; + uint32_t rdyFlag = 0; + do{ + rdyFlag = GCD->READY; + }while(!rdyFlag); + return GCD->RES; +} + +void calcPrintGCD(uint32_t a, uint32_t b){ + uint32_t myGCD = 0; + char buf[5] = { 0x00 }; + char aBuf[11] = { 0x00 }; + char bBuf[11] = { 0x00 }; + itoa(a, aBuf, 10); + itoa(b, bBuf, 10); + print("gcd(");print(aBuf);print(",");print(bBuf);println("):"); + myGCD = gcd(a,b); + itoa(myGCD, buf, 10); + println(buf); +} + +void main() { + GPIO_A->OUTPUT_ENABLE = 0x0000000F; + GPIO_A->OUTPUT = 0x00000001; + println("hello gcd world"); + const int nleds = 4; + const int nloops = 2000000; + + GCD->VALID = 0x00000000; + while(GCD->READY); + + calcPrintGCD(1, 123913); + calcPrintGCD(461952, 116298); + calcPrintGCD(461952, 116298); + calcPrintGCD(461952, 116298); + + while(1){ + for(unsigned int i=0;iOUTPUT = 1<OUTPUT = (1<<(nleds-1))>>i; + delay(nloops); + } + } +} + +void irqCallback(){ +} diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.h new file mode 100644 index 000000000..31cb9c035 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/main.h @@ -0,0 +1,78 @@ + +//---------------------------- +// integer to ascii (itoa) with util functions +//---------------------------- + +// function to swap two numbers +void swap(char *x, char *y) { + char t = *x; *x = *y; *y = t; +} + +// function to reverse buffer[i..j] +char* reverse(char *buffer, int i, int j) { + while (i < j) + swap(&buffer[i++], &buffer[j--]); + return buffer; +} + +// Iterative function to implement itoa() function in C +char* itoa(int value, char* buffer, int base) { + // invalid input + if (base < 2 || base > 32) + return buffer; + // consider absolute value of number + int n = (value < 0) ? -value : value; + int i = 0; + while (n) { + int r = n % base; + if (r >= 10) + buffer[i++] = 65 + (r - 10); + else + buffer[i++] = 48 + r; + n = n / base; + } + + // if number is 0 + if (i == 0) + buffer[i++] = '0'; + + // If base is 10 and value is negative, the resulting string + // is preceded with a minus sign (-) + // With any other base, value is always considered unsigned + if (value < 0 && base == 10) + buffer[i++] = '-'; + + buffer[i] = '\0'; // null terminate string + + // reverse the string and return it + return reverse(buffer, 0, i - 1); +} + +//---------------------------- +// print, println, dbgprint +//---------------------------- + +void print(const char*str){ + while(*str){ + uart_write(UART,*str); + str++; + } +} +void println(const char*str){ + print(str); + uart_write(UART,'\n'); +} + +void dbgPrintln(const char*str){ + #if DEBUG == 1 + println(str); + #else + void; + #endif +} + +void delay(uint32_t loops){ + for(int i=0;iOUTPUT; + } +} \ No newline at end of file diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/murax.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/murax.h new file mode 100644 index 000000000..9d7b7e7a7 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/murax.h @@ -0,0 +1,20 @@ +#ifndef __MURAX_H__ +#define __MURAX_H__ + +#include "timer.h" +#include "prescaler.h" +#include "interrupt.h" +#include "gpio.h" +#include "uart.h" +#include "gcd.h" + +#define GPIO_A ((Gpio_Reg*)(0xF0000000)) +#define TIMER_PRESCALER ((Prescaler_Reg*)0xF0020000) +#define TIMER_INTERRUPT ((InterruptCtrl_Reg*)0xF0020010) +#define TIMER_A ((Timer_Reg*)0xF0020040) +#define TIMER_B ((Timer_Reg*)0xF0020050) +#define UART ((Uart_Reg*)(0xF0010000)) +#define GCD ((Gcd_Reg*)(0xF0030000)) + + +#endif /* __MURAX_H__ */ diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/prescaler.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/prescaler.h new file mode 100644 index 000000000..6bd9694a7 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/prescaler.h @@ -0,0 +1,16 @@ +#ifndef PRESCALERCTRL_H_ +#define PRESCALERCTRL_H_ + +#include + + +typedef struct +{ + volatile uint32_t LIMIT; +} Prescaler_Reg; + +static void prescaler_init(Prescaler_Reg* reg){ + +} + +#endif /* PRESCALERCTRL_H_ */ diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/timer.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/timer.h new file mode 100644 index 000000000..1577535c5 --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/timer.h @@ -0,0 +1,20 @@ +#ifndef TIMERCTRL_H_ +#define TIMERCTRL_H_ + +#include + + +typedef struct +{ + volatile uint32_t CLEARS_TICKS; + volatile uint32_t LIMIT; + volatile uint32_t VALUE; +} Timer_Reg; + +static void timer_init(Timer_Reg *reg){ + reg->CLEARS_TICKS = 0; + reg->VALUE = 0; +} + + +#endif /* TIMERCTRL_H_ */ diff --git a/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/uart.h b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/uart.h new file mode 100644 index 000000000..c3a30a56f --- /dev/null +++ b/doc/gcdPeripheral/src/main/c/murax/gcd_world/src/uart.h @@ -0,0 +1,42 @@ +#ifndef UART_H_ +#define UART_H_ + + +typedef struct +{ + volatile uint32_t DATA; + volatile uint32_t STATUS; + volatile uint32_t CLOCK_DIVIDER; + volatile uint32_t FRAME_CONFIG; +} Uart_Reg; + +enum UartParity {NONE = 0,EVEN = 1,ODD = 2}; +enum UartStop {ONE = 0,TWO = 1}; + +typedef struct { + uint32_t dataLength; + enum UartParity parity; + enum UartStop stop; + uint32_t clockDivider; +} Uart_Config; + +static uint32_t uart_writeAvailability(Uart_Reg *reg){ + return (reg->STATUS >> 16) & 0xFF; +} +static uint32_t uart_readOccupancy(Uart_Reg *reg){ + return reg->STATUS >> 24; +} + +static void uart_write(Uart_Reg *reg, uint32_t data){ + while(uart_writeAvailability(reg) == 0); + reg->DATA = data; +} + +static void uart_applyConfig(Uart_Reg *reg, Uart_Config *config){ + reg->CLOCK_DIVIDER = config->clockDivider; + reg->FRAME_CONFIG = ((config->dataLength-1) << 0) | (config->parity << 8) | (config->stop << 16); +} + +#endif /* UART_H_ */ + + diff --git a/doc/gcdPeripheral/src/main/scala/vexriscv/demo/Murax.scala b/doc/gcdPeripheral/src/main/scala/vexriscv/demo/Murax.scala new file mode 100644 index 000000000..f3d4f6cd0 --- /dev/null +++ b/doc/gcdPeripheral/src/main/scala/vexriscv/demo/Murax.scala @@ -0,0 +1,559 @@ +package vexriscv.demo + +import spinal.core._ +import spinal.lib._ +import spinal.lib.bus.amba3.apb._ +import spinal.lib.bus.misc.SizeMapping +import spinal.lib.bus.simple.PipelinedMemoryBus +import spinal.lib.com.jtag.Jtag +import spinal.lib.com.spi.ddr.SpiXdrMaster +import spinal.lib.com.uart._ +import spinal.lib.io.{InOutWrapper, TriStateArray} +import spinal.lib.misc.{InterruptCtrl, Prescaler, Timer} +import spinal.lib.soc.pinsec.{PinsecTimerCtrl, PinsecTimerCtrlExternal} +import vexriscv.plugin._ +import vexriscv.{VexRiscv, VexRiscvConfig, plugin} +import spinal.lib.com.spi.ddr._ +import spinal.lib.bus.simple._ +import scala.collection.mutable.ArrayBuffer +import vexriscv.periph.gcd._ +import vexriscv.periph.tasks.gen._ +import vexriscv.periph.tasks.map._ +import vexriscv.periph.tasks.sort._ +import vexriscv.periph.tasks.max._ +import vexriscv.periph.tasks.sum._ +import vexriscv.periph.tasks.hash._ + +/** Created by PIC32F_USER on 28/07/2017. + * + * Murax is a very light SoC which could work without any external component. + * - ICE40-hx8k + icestorm => 53 Mhz, 2142 LC + * - 0.37 DMIPS/Mhz + * - 8 kB of on-chip ram + * - JTAG debugger (eclipse/GDB/openocd ready) + * - Interrupt support + * - APB bus for peripherals + * - 32 GPIO pin + * - one 16 bits prescaler, two 16 bits timers + * - one UART with tx/rx fifo + */ + +case class MuraxConfig( + coreFrequency: HertzNumber, + onChipRamSize: BigInt, + onChipRamHexFile: String, + pipelineDBus: Boolean, + pipelineMainBus: Boolean, + pipelineApbBridge: Boolean, + gpioWidth: Int, + uartCtrlConfig: UartCtrlMemoryMappedConfig, + xipConfig: SpiXdrMasterCtrl.MemoryMappingParameters, + hardwareBreakpointCount: Int, + cpuPlugins: ArrayBuffer[Plugin[VexRiscv]] +) { + require( + pipelineApbBridge || pipelineMainBus, + "At least pipelineMainBus or pipelineApbBridge should be enable to avoid wipe transactions" + ) + val genXip = xipConfig != null + +} + +object MuraxConfig { + def default: MuraxConfig = default(false, false) + def default(withXip: Boolean = false, bigEndian: Boolean = false) = + MuraxConfig( + coreFrequency = 12 MHz, + onChipRamSize = 8 kB, + onChipRamHexFile = null, + pipelineDBus = true, + pipelineMainBus = false, + pipelineApbBridge = true, + gpioWidth = 32, + xipConfig = ifGen(withXip)( + SpiXdrMasterCtrl.MemoryMappingParameters( + SpiXdrMasterCtrl + .Parameters(8, 12, SpiXdrParameter(2, 2, 1)) + .addFullDuplex(0, 1, false), + cmdFifoDepth = 32, + rspFifoDepth = 32, + xip = SpiXdrMasterCtrl + .XipBusParameters(addressWidth = 24, lengthWidth = 2) + ) + ), + hardwareBreakpointCount = if (withXip) 3 else 0, + cpuPlugins = ArrayBuffer( //DebugPlugin added by the toplevel + new IBusSimplePlugin( + resetVector = if (withXip) 0xf001e000L else 0x80000000L, + cmdForkOnSecondStage = true, + cmdForkPersistence = withXip, //Required by the Xip controller + prediction = NONE, + catchAccessFault = false, + compressedGen = false, + bigEndian = bigEndian + ), + new DBusSimplePlugin( + catchAddressMisaligned = false, + catchAccessFault = false, + earlyInjection = false, + bigEndian = bigEndian + ), + new CsrPlugin( + CsrPluginConfig.smallest(mtvecInit = + if (withXip) 0xe0040020L else 0x80000020L + ) + ), + new DecoderSimplePlugin( + catchIllegalInstruction = false + ), + new RegFilePlugin( + regFileReadyKind = plugin.SYNC, + zeroBoot = false + ), + new IntAluPlugin, + new SrcPlugin( + separatedAddSub = false, + executeInsertion = false + ), + new LightShifterPlugin, + new HazardSimplePlugin( + bypassExecute = false, + bypassMemory = false, + bypassWriteBack = false, + bypassWriteBackBuffer = false, + pessimisticUseSrc = false, + pessimisticWriteRegFile = false, + pessimisticAddressMatch = false + ), + new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = false + ), + new YamlPlugin("cpu0.yaml") + ), + uartCtrlConfig = UartCtrlMemoryMappedConfig( + uartCtrlConfig = UartCtrlGenerics( + dataWidthMax = 8, + clockDividerWidth = 20, + preSamplingSize = 1, + samplingSize = 3, + postSamplingSize = 1 + ), + initConfig = UartCtrlInitConfig( + baudrate = 115200, + dataLength = 7, //7 => 8 bits + parity = UartParityType.NONE, + stop = UartStopType.ONE + ), + busCanWriteClockDividerConfig = false, + busCanWriteFrameConfig = false, + txFifoDepth = 16, + rxFifoDepth = 16 + ) + ) + + def fast = { + val config = default + + //Replace HazardSimplePlugin to get datapath bypass + config.cpuPlugins( + config.cpuPlugins.indexWhere(_.isInstanceOf[HazardSimplePlugin]) + ) = new HazardSimplePlugin( + bypassExecute = true, + bypassMemory = true, + bypassWriteBack = true, + bypassWriteBackBuffer = true + ) +// config.cpuPlugins(config.cpuPlugins.indexWhere(_.isInstanceOf[LightShifterPlugin])) = new FullBarrelShifterPlugin() + + config + } +} + +case class Murax(config: MuraxConfig) extends Component { + import config._ + + val io = new Bundle { + //Clocks / reset + val asyncReset = in Bool () + val mainClk = in Bool () + + //Main components IO + val jtag = slave(Jtag()) + + //Peripherals IO + val gpioA = master(TriStateArray(gpioWidth bits)) + val uart = master(Uart()) + + val xip = ifGen(genXip)(master(SpiXdrMaster(xipConfig.ctrl.spi))) + } + + val resetCtrlClockDomain = ClockDomain( + clock = io.mainClk, + config = ClockDomainConfig( + resetKind = BOOT + ) + ) + + val resetCtrl = new ClockingArea(resetCtrlClockDomain) { + val mainClkResetUnbuffered = False + + //Implement an counter to keep the reset axiResetOrder high 64 cycles + // Also this counter will automatically do a reset when the system boot. + val systemClkResetCounter = Reg(UInt(6 bits)) init (0) + when(systemClkResetCounter =/= U(systemClkResetCounter.range -> true)) { + systemClkResetCounter := systemClkResetCounter + 1 + mainClkResetUnbuffered := True + } + when(BufferCC(io.asyncReset)) { + systemClkResetCounter := 0 + } + + //Create all reset used later in the design + val mainClkReset = RegNext(mainClkResetUnbuffered) + val systemReset = RegNext(mainClkResetUnbuffered) + } + + val systemClockDomain = ClockDomain( + clock = io.mainClk, + reset = resetCtrl.systemReset, + frequency = FixedFrequency(coreFrequency) + ) + + val debugClockDomain = ClockDomain( + clock = io.mainClk, + reset = resetCtrl.mainClkReset, + frequency = FixedFrequency(coreFrequency) + ) + + val system = new ClockingArea(systemClockDomain) { + val pipelinedMemoryBusConfig = PipelinedMemoryBusConfig( + addressWidth = 32, + dataWidth = 32 + ) + + val bigEndianDBus = config.cpuPlugins.exists(_ match { + case plugin: DBusSimplePlugin => plugin.bigEndian + case _ => false + }) + + //Arbiter of the cpu dBus/iBus to drive the mainBus + //Priority to dBus, !! cmd transactions can change on the fly !! + val mainBusArbiter = + new MuraxMasterArbiter(pipelinedMemoryBusConfig, bigEndianDBus) + + //Instanciate the CPU + val cpu = new VexRiscv( + config = VexRiscvConfig( + plugins = cpuPlugins += new DebugPlugin( + debugClockDomain, + hardwareBreakpointCount + ) + ) + ) + + //Checkout plugins used to instanciate the CPU to connect them to the SoC + val timerInterrupt = False + val externalInterrupt = False + for (plugin <- cpu.plugins) plugin match { + case plugin: IBusSimplePlugin => + mainBusArbiter.io.iBus.cmd <> plugin.iBus.cmd + mainBusArbiter.io.iBus.rsp <> plugin.iBus.rsp + case plugin: DBusSimplePlugin => { + if (!pipelineDBus) + mainBusArbiter.io.dBus <> plugin.dBus + else { + mainBusArbiter.io.dBus.cmd << plugin.dBus.cmd.halfPipe() + mainBusArbiter.io.dBus.rsp <> plugin.dBus.rsp + } + } + case plugin: CsrPlugin => { + plugin.externalInterrupt := externalInterrupt + plugin.timerInterrupt := timerInterrupt + } + case plugin: DebugPlugin => + plugin.debugClockDomain { + resetCtrl.systemReset setWhen (RegNext(plugin.io.resetOut)) + io.jtag <> plugin.io.bus.fromJtag() + } + case _ => + } + + //****** MainBus slaves ******** + val mainBusMapping = ArrayBuffer[(PipelinedMemoryBus, SizeMapping)]() + val ram = new MuraxPipelinedMemoryBusRam( + onChipRamSize = onChipRamSize, + onChipRamHexFile = onChipRamHexFile, + pipelinedMemoryBusConfig = pipelinedMemoryBusConfig, + bigEndian = bigEndianDBus + ) + mainBusMapping += ram.io.bus -> (0x80000000L, onChipRamSize) + + val apbBridge = new PipelinedMemoryBusToApbBridge( + apb3Config = Apb3Config( + addressWidth = 20, + dataWidth = 32 + ), + pipelineBridge = pipelineApbBridge, + pipelinedMemoryBusConfig = pipelinedMemoryBusConfig + ) + mainBusMapping += apbBridge.io.pipelinedMemoryBus -> (0xf0000000L, 1 MB) + + //******** APB peripherals ********* + val apbMapping = ArrayBuffer[(Apb3, SizeMapping)]() + val gpioACtrl = Apb3Gpio(gpioWidth = gpioWidth, withReadSync = true) + io.gpioA <> gpioACtrl.io.gpio + apbMapping += gpioACtrl.io.apb -> (0x00000, 4 kB) + + val uartCtrl = Apb3UartCtrl(uartCtrlConfig) + uartCtrl.io.uart <> io.uart + externalInterrupt setWhen (uartCtrl.io.interrupt) + apbMapping += uartCtrl.io.apb -> (0x10000, 4 kB) + + val timer = new MuraxApb3Timer() + timerInterrupt setWhen (timer.io.interrupt) + apbMapping += timer.io.apb -> (0x20000, 4 kB) + + val gcd = new Apb3GCDCtrl( + apb3Config = Apb3Config( + addressWidth = 20, + dataWidth = 32 + ) + ) + apbMapping += gcd.io.apb -> (0x30000, 1 kB) + + val xip = ifGen(genXip)(new Area { + val ctrl = Apb3SpiXdrMasterCtrl(xipConfig) + ctrl.io.spi <> io.xip + externalInterrupt setWhen (ctrl.io.interrupt) + apbMapping += ctrl.io.apb -> (0x1f000, 4 kB) + + val accessBus = new PipelinedMemoryBus(PipelinedMemoryBusConfig(24, 32)) + mainBusMapping += accessBus -> (0xe0000000L, 16 MB) + + ctrl.io.xip.fromPipelinedMemoryBus() << accessBus + val bootloader = Apb3Rom("src/main/c/murax/xipBootloader/crt.bin") + apbMapping += bootloader.io.apb -> (0x1e000, 4 kB) + }) + + //******** Memory mappings ********* + val apbDecoder = Apb3Decoder( + master = apbBridge.io.apb, + slaves = apbMapping + ) + + val mainBusDecoder = new Area { + val logic = new MuraxPipelinedMemoryBusDecoder( + master = mainBusArbiter.io.masterBus, + specification = mainBusMapping, + pipelineMaster = pipelineMainBus + ) + } + } +} + +object Murax { + def main(args: Array[String]) { + SpinalVerilog(Murax(MuraxConfig.default)) + } +} + +object Murax_iCE40_hx8k_breakout_board_xip { + + case class SB_GB() extends BlackBox { + val USER_SIGNAL_TO_GLOBAL_BUFFER = in Bool () + val GLOBAL_BUFFER_OUTPUT = out Bool () + } + + case class SB_IO_SCLK() extends BlackBox { + addGeneric("PIN_TYPE", B"010000") + val PACKAGE_PIN = out Bool () + val OUTPUT_CLK = in Bool () + val CLOCK_ENABLE = in Bool () + val D_OUT_0 = in Bool () + val D_OUT_1 = in Bool () + setDefinitionName("SB_IO") + } + + case class SB_IO_DATA() extends BlackBox { + addGeneric("PIN_TYPE", B"110000") + val PACKAGE_PIN = inout(Analog(Bool)) + val CLOCK_ENABLE = in Bool () + val INPUT_CLK = in Bool () + val OUTPUT_CLK = in Bool () + val OUTPUT_ENABLE = in Bool () + val D_OUT_0 = in Bool () + val D_OUT_1 = in Bool () + val D_IN_0 = out Bool () + val D_IN_1 = out Bool () + setDefinitionName("SB_IO") + } + + case class Murax_iCE40_hx8k_breakout_board_xip() extends Component { + val io = new Bundle { + val mainClk = in Bool () + val jtag_tck = in Bool () + val jtag_tdi = in Bool () + val jtag_tdo = out Bool () + val jtag_tms = in Bool () + val uart_txd = out Bool () + val uart_rxd = in Bool () + + val mosi = inout(Analog(Bool)) + val miso = inout(Analog(Bool)) + val sclk = out Bool () + val spis = out Bool () + + val led = out Bits (8 bits) + } + val murax = Murax( + MuraxConfig.default(withXip = true).copy(onChipRamSize = 8 kB) + ) + murax.io.asyncReset := False + + val mainClkBuffer = SB_GB() + mainClkBuffer.USER_SIGNAL_TO_GLOBAL_BUFFER <> io.mainClk + mainClkBuffer.GLOBAL_BUFFER_OUTPUT <> murax.io.mainClk + + val jtagClkBuffer = SB_GB() + jtagClkBuffer.USER_SIGNAL_TO_GLOBAL_BUFFER <> io.jtag_tck + jtagClkBuffer.GLOBAL_BUFFER_OUTPUT <> murax.io.jtag.tck + + io.led <> murax.io.gpioA.write(7 downto 0) + + murax.io.jtag.tdi <> io.jtag_tdi + murax.io.jtag.tdo <> io.jtag_tdo + murax.io.jtag.tms <> io.jtag_tms + murax.io.gpioA.read <> 0 + murax.io.uart.txd <> io.uart_txd + murax.io.uart.rxd <> io.uart_rxd + + val xip = new ClockingArea(murax.systemClockDomain) { + RegNext(murax.io.xip.ss.asBool) <> io.spis + + val sclkIo = SB_IO_SCLK() + sclkIo.PACKAGE_PIN <> io.sclk + sclkIo.CLOCK_ENABLE := True + + sclkIo.OUTPUT_CLK := ClockDomain.current.readClockWire + sclkIo.D_OUT_0 <> murax.io.xip.sclk.write(0) + sclkIo.D_OUT_1 <> RegNext(murax.io.xip.sclk.write(1)) + + val datas = + for ((data, pin) <- (murax.io.xip.data, List(io.mosi, io.miso)).zipped) + yield new Area { + val dataIo = SB_IO_DATA() + dataIo.PACKAGE_PIN := pin + dataIo.CLOCK_ENABLE := True + + dataIo.OUTPUT_CLK := ClockDomain.current.readClockWire + dataIo.OUTPUT_ENABLE <> data.writeEnable + dataIo.D_OUT_0 <> data.write(0) + dataIo.D_OUT_1 <> RegNext(data.write(1)) + + dataIo.INPUT_CLK := ClockDomain.current.readClockWire + data.read(0) := dataIo.D_IN_0 + data.read(1) := RegNext(dataIo.D_IN_1) + } + } + + } + + def main(args: Array[String]) { + SpinalVerilog(Murax_iCE40_hx8k_breakout_board_xip()) + } +} + +object MuraxDhrystoneReady { + def main(args: Array[String]) { + SpinalVerilog(Murax(MuraxConfig.fast.copy(onChipRamSize = 256 kB))) + } +} + +object MuraxDhrystoneReadyMulDivStatic { + def main(args: Array[String]) { + SpinalVerilog({ + val config = MuraxConfig.fast.copy(onChipRamSize = 256 kB) + config.cpuPlugins += new MulPlugin + config.cpuPlugins += new DivPlugin + config.cpuPlugins.remove( + config.cpuPlugins.indexWhere(_.isInstanceOf[BranchPlugin]) + ) + config.cpuPlugins += new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = false + ) + config.cpuPlugins += new IBusSimplePlugin( + resetVector = 0x80000000L, + cmdForkOnSecondStage = true, + cmdForkPersistence = false, + prediction = STATIC, + catchAccessFault = false, + compressedGen = false + ) + config.cpuPlugins.remove( + config.cpuPlugins.indexWhere(_.isInstanceOf[LightShifterPlugin]) + ) + config.cpuPlugins += new FullBarrelShifterPlugin + Murax(config) + }) + } +} + +//Will blink led and echo UART RX to UART TX (in the verilator sim, type some text and press enter to send UART frame to the Murax RX pin) +object MuraxWithRamInit { + def main(args: Array[String]) { + SpinalVerilog( + Murax( + MuraxConfig.default.copy( + onChipRamSize = 4 kB, + onChipRamHexFile = "src/main/c/murax/gcd_world/build/gcd_world.hex" + ) + ) + ) + .printPruned() + } +} + +object MuraxWithRamInitSynth { + def main(args: Array[String]) { + val config = SpinalConfig( + targetDirectory = "synth", + defaultClockDomainFrequency = FixedFrequency(12 MHz) + ) + config + .generateVerilog( + Murax( + MuraxConfig.default.copy( + onChipRamSize = 4 kB, + onChipRamHexFile = "src/main/c/murax/gcd_world/build/gcd_world.hex" + ) + ) + ) + .printPruned() + } +} + +object Murax_arty { + def main(args: Array[String]) { + val hex = "src/main/c/murax/hello_world/build/hello_world.hex" + SpinalVerilog( + Murax( + MuraxConfig + .default(false) + .copy( + coreFrequency = 100 MHz, + onChipRamSize = 32 kB, + onChipRamHexFile = hex + ) + ) + ) + } +} + +object MuraxAsicBlackBox extends App { + println("Warning this soc do not has any rom to boot on.") + val config = SpinalConfig() + config.addStandardMemBlackboxing(blackboxAll) + config.generateVerilog(Murax(MuraxConfig.default())) +} diff --git a/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/Apb3GCDCtrl.scala b/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/Apb3GCDCtrl.scala new file mode 100644 index 000000000..2ec740163 --- /dev/null +++ b/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/Apb3GCDCtrl.scala @@ -0,0 +1,39 @@ +package vexriscv.periph.gcd + +import spinal.core._ +import spinal.lib._ +import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config, Apb3SlaveFactory} +import spinal.lib.eda.altera.QSysify +import spinal.lib.slave + +object Apb3GCDCtrl { + def getApb3Config = Apb3Config( + addressWidth = 5, + dataWidth = 32, + selWidth = 1, + useSlaveError = false + ) +} + +class Apb3GCDCtrl(apb3Config : Apb3Config) extends Component { + val io = new Bundle { + val apb = slave(Apb3(Apb3GCDCtrl.getApb3Config)) + // maybe later + // val interrupt = out Bool + } + val gcdCtrl = new GCDTop() + val apbCtrl = Apb3SlaveFactory(io.apb) + apbCtrl.driveAndRead(gcdCtrl.io.a, address=0) + apbCtrl.driveAndRead(gcdCtrl.io.b, address=4) + // when result of calculation ready, synchronize it into memory mapped register + val resSyncBuf = RegNextWhen(gcdCtrl.io.res, gcdCtrl.io.ready) + apbCtrl.read(resSyncBuf, address=8) + // if result is read, it will be consumed, set ready to 0 + apbCtrl.onRead(8)(resSyncBuf := 0) + apbCtrl.onRead(8)(rdySyncBuf := False) + // synchronize ready signal into memory mapped register + val rdySyncBuf = RegNextWhen(gcdCtrl.io.ready, gcdCtrl.io.ready) + apbCtrl.read(rdySyncBuf, address=12) + // set valid based on memory mapped register but clear/consume it after 1 cycle b): + * a := a - b + * else if(b > a): + * b := b - a + * else: + * done := True + */ + //registers + val regA = Reg(UInt(32 bits)) init(0) + val regB = Reg(UInt(32 bits)) init(0) + // compare + val xGTy = regA > regB + val xLTy = regA < regB + // mux + val chX = io.dataCtrl.selL ? regB | regA + val chY = io.dataCtrl.selR ? regB | regA + // subtract + val subXY = chX - chY + // load logic + when(io.dataCtrl.init){ + regA := io.a + regB := io.b + } + when(io.dataCtrl.loadA){ + regA := subXY + } + when(io.dataCtrl.loadB){ + regB := subXY + } + io.dataCtrl.cmpAgtB := xGTy + io.dataCtrl.cmpAltB := xLTy + io.res := regA +} \ No newline at end of file diff --git a/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTop.scala b/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTop.scala new file mode 100644 index 000000000..654e9b8fc --- /dev/null +++ b/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTop.scala @@ -0,0 +1,46 @@ +package vexriscv.periph.gcd + +import spinal.core._ +import spinal.lib._ +import spinal.lib.IMasterSlave + +case class GCDDataControl() extends Bundle with IMasterSlave{ + val cmpAgtB = Bool + val cmpAltB = Bool + val loadA = Bool + val loadB = Bool + val init = Bool + val selL = Bool + val selR = Bool + // define <> semantic + override def asMaster(): Unit = { + // as controller: output, input + out(loadA, loadB, selL, selR, init) + in(cmpAgtB, cmpAltB) + } +} + +//Hardware definition +class GCDTop() extends Component { + val io = new Bundle { + val valid = in Bool() + val ready = out Bool() + val a = in(UInt(32 bits)) + val b = in(UInt(32 bits)) + val res = out(UInt(32 bits)) + } + val gcdCtr = new GCDCtrl() + gcdCtr.io.valid := io.valid + io.ready := gcdCtr.io.ready + val gcdDat = new GCDData() + gcdDat.io.a := io.a + gcdDat.io.b := io.b + io.res := gcdDat.io.res + gcdCtr.io.dataCtrl <> gcdDat.io.dataCtrl +} + +object GCDTopVerilog { + def main(args: Array[String]) { + SpinalVerilog(new GCDTop) + } +} \ No newline at end of file diff --git a/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTopSim.scala b/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTopSim.scala new file mode 100644 index 000000000..53ea8dc77 --- /dev/null +++ b/doc/gcdPeripheral/src/main/scala/vexriscv/periph/gcd/GCDTopSim.scala @@ -0,0 +1,52 @@ +package vexriscv.periph.gcd + +import spinal.core._ +import spinal.sim._ +import spinal.core.sim._ + +//import scala.util.Random +import java.util.concurrent.ThreadLocalRandom +object GCDTopSim { + def main(args: Array[String]) { + + SimConfig.withWave.doSim(new GCDTop()){dut => + // SimConfig.doSim(new GCDTop()){dut => + def gcd(a: Long,b: Long): Long = { + if(b==0) a else gcd(b, a%b) + } + def RndNextUInt32(): Long = { + ThreadLocalRandom.current().nextLong(Math.pow(2, 32).toLong - 1) + } + var a = 0L + var b = 0L + var model = 0L + dut.io.a #= 0 + dut.io.b #= 0 + dut.io.valid #= false + + dut.clockDomain.forkStimulus(period = 10) + dut.clockDomain.waitRisingEdge() + + for(idx <- 0 to 500){ + // generate 2 random ints + a = RndNextUInt32() + b = RndNextUInt32() + // calculate the model value (software) + model = gcd(a,b) + // apply stimulus with random ints + dut.io.a #= a + dut.io.b #= b + dut.io.valid #= true + dut.clockDomain.waitRisingEdge() + dut.io.valid #= false + // wait until calculation of hardware is done + waitUntil(dut.io.ready.toBoolean) + assert( + assertion = (dut.io.res.toBigInt == model), + message = "test " + idx + " failed. Expected " + model + ", retrieved: " + dut.io.res.toBigInt + ) + waitUntil(!dut.io.ready.toBoolean) + } + } + } +} From 17007586e899d0301872a079d278ff06b678385d Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 11 Apr 2022 11:59:41 +0200 Subject: [PATCH 05/35] #241 Fix Murax/Briey TB timeouts --- src/test/cpp/briey/main.cpp | 2 -- src/test/cpp/common/framework.h | 7 +++++-- src/test/cpp/murax/main.cpp | 4 +--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/cpp/briey/main.cpp b/src/test/cpp/briey/main.cpp index c0e165a22..bebe88007 100644 --- a/src/test/cpp/briey/main.cpp +++ b/src/test/cpp/briey/main.cpp @@ -466,8 +466,6 @@ int main(int argc, char **argv, char **env) { uint64_t duration = timer_end(startedAt); cout << endl << "****************************************************************" << endl; - cout << "Had simulate " << workspaceCycles << " clock cycles in " << duration*1e-9 << " s (" << workspaceCycles / (duration*1e-9) << " Khz)" << endl; - cout << "****************************************************************" << endl << endl; exit(0); diff --git a/src/test/cpp/common/framework.h b/src/test/cpp/common/framework.h index 42c1f34ad..ed419adc2 100644 --- a/src/test/cpp/common/framework.h +++ b/src/test/cpp/common/framework.h @@ -127,7 +127,6 @@ class AsyncReset : public TimeProcess{ class success : public std::exception { }; -static uint32_t workspaceCycles = 0; template class Workspace{ public: @@ -180,7 +179,7 @@ template class Workspace{ #endif } - Workspace* run(uint32_t timeout = 5000){ + Workspace* run(double timeout = 1e6){ // init trace dump #ifdef TRACE @@ -205,6 +204,10 @@ template class Workspace{ if(p->wakeEnable && p->wakeDelay < delay) delay = p->wakeDelay; + if(time*timeToSec > timeout){ + printf("Simulation timeout triggered (%f)\n", time*timeToSec); + fail(); + } if(delay == ~0l){ fail(); } diff --git a/src/test/cpp/murax/main.cpp b/src/test/cpp/murax/main.cpp index 9738e8479..735875fb6 100644 --- a/src/test/cpp/murax/main.cpp +++ b/src/test/cpp/murax/main.cpp @@ -54,12 +54,10 @@ int main(int argc, char **argv, char **env) { printf("BOOT\n"); timespec startedAt = timer_start(); - MuraxWorkspace().run(100e6); + MuraxWorkspace().run(1e9); uint64_t duration = timer_end(startedAt); cout << endl << "****************************************************************" << endl; - cout << "Had simulate " << workspaceCycles << " clock cycles in " << duration*1e-9 << " s (" << workspaceCycles / (duration*1e-9) << " Khz)" << endl; - cout << "****************************************************************" << endl << endl; exit(0); From 9772e6775dde1f2b62f1ceb3525034d5e6782142 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 27 Apr 2022 16:12:56 +0200 Subject: [PATCH 06/35] readme now document FPU / openocd limitations --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 1a8864be2..70e63ae96 100644 --- a/README.md +++ b/README.md @@ -750,6 +750,12 @@ Fpu 64/32 bits -> Artix 7 FMax -> 165 Mhz 3728 LUT 3175 FF ``` +Note that if you want to debug FPU code via the openocd_riscv.vexriscv target, you need to use the GDB from : + +https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-20171231-x86_64-linux-centos6.tar.gz + +More recent versions of gdb will not detect the FPU. Also, the openocd_riscv.vexriscv can't read CSR/FPU registers, so to have visibility on the floating points values, you need to compile your code in -O0, which will force values to be stored in memory (and so, be visible) + ### Plugins This chapter describes the currently implemented plugins. From e0eb00573c524fd3b35b13c1c07775ef0b7e0f26 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 9 May 2022 11:33:15 +0200 Subject: [PATCH 07/35] SpinalHDL 1.7.0a --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 614648845..0206cffb0 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -val spinalVersion = "1.7.0" +val spinalVersion = "1.7.0a" lazy val root = (project in file(".")). settings( From 4fff62d3feeb05edcea46e66b25de0ecf3f274fa Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 11 May 2022 14:10:11 +0200 Subject: [PATCH 08/35] Fix RVC step by step triggering next instruction branch predictor --- src/main/scala/vexriscv/Services.scala | 1 + src/main/scala/vexriscv/plugin/DebugPlugin.scala | 4 ++++ src/main/scala/vexriscv/plugin/Fetcher.scala | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index 8a291d646..140c69bca 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -17,6 +17,7 @@ trait IBusFetcher{ def pcValid(stage : Stage) : Bool def getInjectionPort() : Stream[Bits] def withRvc() : Boolean + def forceNoDecode() : Unit } diff --git a/src/main/scala/vexriscv/plugin/DebugPlugin.scala b/src/main/scala/vexriscv/plugin/DebugPlugin.scala index 87d4f5693..a0b4619db 100644 --- a/src/main/scala/vexriscv/plugin/DebugPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DebugPlugin.scala @@ -319,6 +319,10 @@ class DebugPlugin(var debugClockDomain : ClockDomain, hardwareBreakpointCount : if(pipeline.config.withRvc){ val cleanStep = RegNext(stepIt && decode.arbitration.isFiring) init(False) execute.arbitration.flushNext setWhen(cleanStep) + when(cleanStep){ + execute.arbitration.flushNext := True + iBusFetcher.forceNoDecode() + } } io.resetOut := RegNext(resetIt) diff --git a/src/main/scala/vexriscv/plugin/Fetcher.scala b/src/main/scala/vexriscv/plugin/Fetcher.scala index 8b276e132..fdfde8b6d 100644 --- a/src/main/scala/vexriscv/plugin/Fetcher.scala +++ b/src/main/scala/vexriscv/plugin/Fetcher.scala @@ -33,6 +33,7 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, // assert(!(cmdToRspStageCount == 1 && !injectorStage)) assert(!(compressedGen && !decodePcGen)) var fetcherHalt : Bool = null + var forceNoDecodeCond : Bool = null var pcValids : Vec[Bool] = null def pcValid(stage : Stage) = pcValids(pipeline.indexOf(stage)) var incomingInstruction : Bool = null @@ -50,6 +51,7 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, var predictionJumpInterface : Flow[UInt] = null override def haltIt(): Unit = fetcherHalt := True + override def forceNoDecode(): Unit = forceNoDecodeCond := True case class JumpInfo(interface : Flow[UInt], stage: Stage, priority : Int) val jumpInfos = ArrayBuffer[JumpInfo]() override def createJumpInterface(stage: Stage, priority : Int = 0): Flow[UInt] = { @@ -63,6 +65,7 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, // var decodeExceptionPort : Flow[ExceptionCause] = null override def setup(pipeline: VexRiscv): Unit = { fetcherHalt = False + forceNoDecodeCond = False incomingInstruction = False if(resetVector == null) externalResetVector = in(UInt(32 bits).setName("externalResetVector")) @@ -408,6 +411,9 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, }) } + Component.current.addPrePopTask(() => { + decode.arbitration.isValid clearWhen(forceNoDecodeCond) + }) //Formal verification signals generation, miss prediction stuff ? val formal = new Area { From 9c768be7af39072d3f2f7cca5c4ae2974ad8d86f Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 16 May 2022 10:36:21 +0200 Subject: [PATCH 09/35] Fix CfuPlugin/VfuPlugin fork duplication https://github.com/google/CFU-Playground/issues/582 --- src/main/scala/vexriscv/plugin/CfuPlugin.scala | 2 +- src/main/scala/vexriscv/plugin/VfuPlugin.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/vexriscv/plugin/CfuPlugin.scala b/src/main/scala/vexriscv/plugin/CfuPlugin.scala index de6daa14f..9a93f6c09 100644 --- a/src/main/scala/vexriscv/plugin/CfuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CfuPlugin.scala @@ -193,7 +193,7 @@ class CfuPlugin(val stageCount : Int, arbitration.haltItself setWhen(scheduleWish && hazard) val hold = RegInit(False) setWhen(schedule) clearWhen(bus.cmd.ready) - val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuckByOthers) + val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuck) insert(CFU_IN_FLIGHT) := schedule || hold || fired bus.cmd.valid := (schedule || hold) && !fired diff --git a/src/main/scala/vexriscv/plugin/VfuPlugin.scala b/src/main/scala/vexriscv/plugin/VfuPlugin.scala index c3048273f..a2c09304f 100644 --- a/src/main/scala/vexriscv/plugin/VfuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/VfuPlugin.scala @@ -100,7 +100,7 @@ class VfuPlugin(val stageCount : Int, arbitration.haltItself setWhen(scheduleWish && hazard) val hold = RegInit(False) setWhen(schedule) clearWhen(bus.cmd.ready) - val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuckByOthers) + val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuck) insert(VFU_IN_FLIGHT) := schedule || hold || fired bus.cmd.valid := (schedule || hold) && !fired From 8d0f7781de05817b2a60e06c7ef4a35b70e3153d Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 17 May 2022 15:27:31 +0200 Subject: [PATCH 10/35] Fix DYNAMIC_TARGET from triggering fetch missprediction while in debug mode #254 --- src/main/scala/vexriscv/plugin/BranchPlugin.scala | 7 +++++++ src/main/scala/vexriscv/plugin/DebugPlugin.scala | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/main/scala/vexriscv/plugin/BranchPlugin.scala b/src/main/scala/vexriscv/plugin/BranchPlugin.scala index eb4c8d03b..24d42fa01 100644 --- a/src/main/scala/vexriscv/plugin/BranchPlugin.scala +++ b/src/main/scala/vexriscv/plugin/BranchPlugin.scala @@ -46,6 +46,7 @@ case class FetchPredictionBus(stage : Stage) extends Bundle { trait PredictionInterface{ def askFetchPrediction() : FetchPredictionBus def askDecodePrediction() : DecodePredictionBus + def inDebugNoFetch() : Unit } @@ -68,6 +69,7 @@ class BranchPlugin(earlyBranch : Boolean, var jumpInterface : Flow[UInt] = null var predictionExceptionPort : Flow[ExceptionCause] = null var branchExceptionPort : Flow[ExceptionCause] = null + var inDebugNoFetchFlag : Bool = null var decodePrediction : DecodePredictionBus = null @@ -84,6 +86,9 @@ class BranchPlugin(earlyBranch : Boolean, decodePrediction } + + override def inDebugNoFetch(): Unit = inDebugNoFetchFlag := True + def hasHazardOnBranch = if(earlyBranch) pipeline.service(classOf[HazardService]).hazardOnExecuteRS else False override def setup(pipeline: VexRiscv): Unit = { @@ -147,6 +152,7 @@ class BranchPlugin(earlyBranch : Boolean, val exceptionService = pipeline.service(classOf[ExceptionService]) branchExceptionPort = exceptionService.newExceptionPort(branchStage) } + inDebugNoFetchFlag = False.setCompositeName(this, "inDebugNoFetchFlag") } override def build(pipeline: VexRiscv): Unit = { @@ -353,6 +359,7 @@ class BranchPlugin(earlyBranch : Boolean, import branchStage._ val predictionMissmatch = fetchPrediction.cmd.hadBranch =/= input(BRANCH_DO) || (input(BRANCH_DO) && input(TARGET_MISSMATCH)) + when(inDebugNoFetchFlag) { predictionMissmatch := input(BRANCH_DO)} fetchPrediction.rsp.wasRight := ! predictionMissmatch fetchPrediction.rsp.finalPc := input(BRANCH_CALC) fetchPrediction.rsp.sourceLastWord := { diff --git a/src/main/scala/vexriscv/plugin/DebugPlugin.scala b/src/main/scala/vexriscv/plugin/DebugPlugin.scala index a0b4619db..01c2acd65 100644 --- a/src/main/scala/vexriscv/plugin/DebugPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DebugPlugin.scala @@ -342,6 +342,10 @@ class DebugPlugin(var debugClockDomain : ClockDomain, hardwareBreakpointCount : case p : PrivilegeService => p.forceMachine() case _ => } + pipeline.plugins.foreach{ + case p : PredictionInterface => p.inDebugNoFetch() + case _ => + } if(pipeline.things.contains(DEBUG_BYPASS_CACHE)) pipeline(DEBUG_BYPASS_CACHE) := True } when(allowEBreak) { From b39557e226ee430783e009981d312b38587f266e Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 17 May 2022 20:44:02 +0200 Subject: [PATCH 11/35] Fix DYNAMIC_TARGET / debug plugin interation corrupting the recoded next pc durring step by step #254 --- src/main/scala/vexriscv/plugin/Fetcher.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/vexriscv/plugin/Fetcher.scala b/src/main/scala/vexriscv/plugin/Fetcher.scala index fdfde8b6d..14450a10a 100644 --- a/src/main/scala/vexriscv/plugin/Fetcher.scala +++ b/src/main/scala/vexriscv/plugin/Fetcher.scala @@ -186,7 +186,7 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, val predictionPcLoad = ifGen(prediction == DYNAMIC_TARGET) (Flow(UInt(32 bits))) if(prediction == DYNAMIC_TARGET) { - when(predictionPcLoad.valid) { + when(predictionPcLoad.valid && !forceNoDecodeCond) { pcReg := predictionPcLoad.payload } } From 8ab9a9b12e5d8881e3a895b31b6a57d076192df0 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 1 Jun 2022 09:53:41 +0200 Subject: [PATCH 12/35] fix VexRiscvRegressionData url --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index ab5c2daa7..4205bb2e9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/test/resources/VexRiscvRegressionData"] path = src/test/resources/VexRiscvRegressionData - url = ../VexRiscvRegressionData.git + url = https://github.com/SpinalHDL/VexRiscvRegressionData.git From 7b9891829a3498c0178eaca660ab95cfc8f93390 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 26 Sep 2022 11:39:58 +0200 Subject: [PATCH 13/35] More bus doc #266 --- README.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/README.md b/README.md index 410d7f989..bcff23916 100644 --- a/README.md +++ b/README.md @@ -868,6 +868,41 @@ Simple and light multi-way instruction cache. Note: If you enable the twoCycleRam option and if wayCount is bigger than one, then the register file plugin should be configured to read the regFile in an asynchronous manner. +The memory bus is defined as : + +```scala +case class InstructionCacheMemCmd(p : InstructionCacheConfig) extends Bundle{ + val address = UInt(p.addressWidth bit) + val size = UInt(log2Up(log2Up(p.bytePerLine) + 1) bits) +} + +case class InstructionCacheMemRsp(p : InstructionCacheConfig) extends Bundle{ + val data = Bits(p.memDataWidth bit) + val error = Bool +} + +case class InstructionCacheMemBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{ + val cmd = Stream (InstructionCacheMemCmd(p)) + val rsp = Flow (InstructionCacheMemRsp(p)) + + override def asMaster(): Unit = { + master(cmd) + slave(rsp) + } +} +``` + +The address is in byte and aligned to the bytePerLine config, the size will always be equal to log2(bytePerLine). + +Note that the cmd stream transaction need to be consumed before starting to send back some rsp transactions (1 cycle minimal latency) + +Some documentation about Stream here : + +https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Libraries/stream.html?highlight=stream + +Flow are the same as Stream but without ready signal. + + #### DecoderSimplePlugin This plugin provides instruction decoding capabilities to other plugins. @@ -1046,6 +1081,75 @@ Multi way cache implementation with writh-through and allocate on read strategy. You can invalidate the whole cache via the 0x500F instruction, and you can invalidate a address range (single line size) via the instruction 0x500F | RS1 << 15 where RS1 should not be X0 and point to one byte of the desired address to invalidate. + +The memory bus is defined as : + +```scala +case class DataCacheMemCmd(p : DataCacheConfig) extends Bundle{ + val wr = Bool + val uncached = Bool + val address = UInt(p.addressWidth bit) + val data = Bits(p.cpuDataWidth bits) + val mask = Bits(p.cpuDataWidth/8 bits) + val size = UInt(p.sizeWidth bits) //... 1 => 2 bytes ... 2 => 4 bytes ... + val exclusive = p.withExclusive generate Bool() + val last = Bool +} +case class DataCacheMemRsp(p : DataCacheConfig) extends Bundle{ + val aggregated = UInt(p.aggregationWidth bits) + val last = Bool() + val data = Bits(p.memDataWidth bit) + val error = Bool + val exclusive = p.withExclusive generate Bool() +} +case class DataCacheInv(p : DataCacheConfig) extends Bundle{ + val enable = Bool() + val address = UInt(p.addressWidth bit) +} +case class DataCacheAck(p : DataCacheConfig) extends Bundle{ + val hit = Bool() +} + +case class DataCacheSync(p : DataCacheConfig) extends Bundle{ + val aggregated = UInt(p.aggregationWidth bits) +} + +case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave{ + val cmd = Stream (DataCacheMemCmd(p)) + val rsp = Flow (DataCacheMemRsp(p)) + + val inv = p.withInvalidate generate Stream(Fragment(DataCacheInv(p))) + val ack = p.withInvalidate generate Stream(Fragment(DataCacheAck(p))) + val sync = p.withInvalidate generate Stream(DataCacheSync(p)) + + override def asMaster(): Unit = { + master(cmd) + slave(rsp) + + if(p.withInvalidate) { + slave(inv) + master(ack) + slave(sync) + } + } +} +``` + +If you don't use memory coherency you can ignore the inv/ack/sync streams, also write cmd should not generate any rsp transaction. + +As the cache is write through, there is no write burst but only individual write transactions. + +The address is in byte and aligned to the bytePerLine config, the size will is encoded as log2(number of bytes in the burst). +last should be set only on the last transaction of a burst. + +Note that the cmd stream transaction need to be consumed before starting to send back some rsp transactions (1 cycle minimal latency) + +Some documentation about Stream here : + +https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Libraries/stream.html?highlight=stream + +Flow are the same as Stream but without ready signal. + #### MulPlugin Implements the multiplication instruction from the RISC-V M extension. Its implementation was done in a FPGA friendly way by using 4 17*17 bit multiplications. From b86047901ae55a3ea0dc892acf1e72c2f0012118 Mon Sep 17 00:00:00 2001 From: buncram Date: Mon, 19 Dec 2022 19:03:33 +0800 Subject: [PATCH 14/35] add flag to expose SATP externally --- .../scala/vexriscv/plugin/MmuPlugin.scala | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/src/main/scala/vexriscv/plugin/MmuPlugin.scala index c984b035d..73efaf040 100644 --- a/src/main/scala/vexriscv/plugin/MmuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/MmuPlugin.scala @@ -41,7 +41,9 @@ case class MmuPortConfig(portTlbSize : Int, latency : Int = 0, earlyRequireMmuLo class MmuPlugin(ioRange : UInt => Bool, virtualRange : UInt => Bool = address => True, // allowUserIo : Boolean = false, - enableMmuInMachineMode : Boolean = false) extends Plugin[VexRiscv] with MemoryTranslator { + enableMmuInMachineMode : Boolean = false, + exportSatp: Boolean = false + ) extends Plugin[VexRiscv] with MemoryTranslator { var dBusAccess : DBusAccess = null val portsInfo = ArrayBuffer[MmuPort]() @@ -91,10 +93,18 @@ class MmuPlugin(ioRange : UInt => Bool, val sum, mxr, mprv = RegInit(False) mprv clearWhen(csrService.xretAwayFromMachine) } - val satp = new Area { - val mode = RegInit(False) - val asid = Reg(Bits(9 bits)) - val ppn = Reg(UInt(20 bits)) + val satp = if(exportSatp) { + new Area { + val mode = out(RegInit(False)) + val asid = out(Reg(Bits(9 bits))) + val ppn = out(Reg(UInt(20 bits))) + } + } else { + new Area { + val mode = RegInit(False) + val asid = Reg(Bits(9 bits)) + val ppn = Reg(UInt(20 bits)) + } } for(offset <- List(CSR.MSTATUS, CSR.SSTATUS)) csrService.rw(offset, 19 -> status.mxr, 18 -> status.sum, 17 -> status.mprv) From bf3521f86a424736168fec1741e4a187fc758cad Mon Sep 17 00:00:00 2001 From: bunnie Date: Tue, 20 Dec 2022 19:25:47 +0800 Subject: [PATCH 15/35] Expand SATP register to 22 bits per spec Vex only implements a 32-bit PA which does not take advantage of the potetnial 32-bit space in Sv32 mode. Very reasonably, Vex simply discards the top two unused bits. However, the spec does require that the register occupy all 22 bits and it is possible for the OS to use the extra bits up top for some bookkeeping purpose. This commit proposes to expand the register to occupy the full 22 bits in case an OS is written to utilize the full width of the register as written in the spec. --- src/main/scala/vexriscv/plugin/MmuPlugin.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/src/main/scala/vexriscv/plugin/MmuPlugin.scala index c984b035d..c17a3879c 100644 --- a/src/main/scala/vexriscv/plugin/MmuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/MmuPlugin.scala @@ -94,7 +94,8 @@ class MmuPlugin(ioRange : UInt => Bool, val satp = new Area { val mode = RegInit(False) val asid = Reg(Bits(9 bits)) - val ppn = Reg(UInt(20 bits)) + // Bottom 20 bits are used in implementation, but top 2 bits are still stored for OS use. + val ppn = Reg(UInt(22 bits)) } for(offset <- List(CSR.MSTATUS, CSR.SSTATUS)) csrService.rw(offset, 19 -> status.mxr, 18 -> status.sum, 17 -> status.mprv) @@ -233,7 +234,8 @@ class MmuPlugin(ioRange : UInt => Bool, } is(State.L1_CMD){ dBusAccess.cmd.valid := True - dBusAccess.cmd.address := csr.satp.ppn @@ vpn(1) @@ U"00" + // RV spec allows for 34-bit phys address in Sv32 mode; we only implement 32 bits and ignore the top 2 bits of satp. + dBusAccess.cmd.address := csr.satp.ppn(19 downto 0) @@ vpn(1) @@ U"00" when(dBusAccess.cmd.ready){ state := State.L1_RSP } From df52fab7d1422c4ddeb964dbb96445150bf3b0b4 Mon Sep 17 00:00:00 2001 From: chiangkd Date: Sat, 24 Dec 2022 20:41:57 +0800 Subject: [PATCH 16/35] Fix incorrect comment --- src/test/cpp/custom/atomic/src/crt.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/cpp/custom/atomic/src/crt.S b/src/test/cpp/custom/atomic/src/crt.S index 1462dd250..ef5cb4119 100644 --- a/src/test/cpp/custom/atomic/src/crt.S +++ b/src/test/cpp/custom/atomic/src/crt.S @@ -15,7 +15,7 @@ trap_entry: csrr x29, mstatus and x29, x29, 0x080 beqz x29, notExternalInterrupt - li x29, 0x1800 //000 disable interrupts + li x29, 0x1800 //1800 disable interrupts csrw mstatus,x29 mret @@ -227,7 +227,7 @@ test7: sw a3, 0(a0) li x29, 0x800 //800 external interrupts csrw mie,x29 - li x29, 0x1808 //008 enable interrupts + li x29, 0x1808 //1808 enable interrupts lr.w a5, (a0) csrw mstatus,x29 //Enable external interrupt (will jump instantly due to testbench setup) nop From 6650d0549da23197a99f0c18334af85a02a10f49 Mon Sep 17 00:00:00 2001 From: chiangkd Date: Thu, 12 Jan 2023 20:51:58 +0800 Subject: [PATCH 17/35] Fix invalid hyperlink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a25ca3f3..28bace2ec 100644 --- a/README.md +++ b/README.md @@ -319,7 +319,7 @@ If you want to get more information about how all this JTAG / GDB stuff work, yo ## Briey SoC As a demonstration, a SoC named Briey is implemented in `src/main/scala/vexriscv/demo/Briey.scala`. This SoC is very similar to -the [Pinsec SoC](https://spinalhdl.github.io/SpinalDoc-RTD/SpinalHDL/Legacy/pinsec/hardware_toplevel.html#): +the [Pinsec SoC](https://spinalhdl.github.io/SpinalDoc-RTD/v1.3.1/SpinalHDL/Legacy/pinsec/hardware_toplevel.html): ![Briey SoC](assets/brieySoc.png?raw=true "") From 2297f8aea0133f3304bdc6493bdb157d66fce63d Mon Sep 17 00:00:00 2001 From: buncram Date: Mon, 16 Jan 2023 02:16:25 +0800 Subject: [PATCH 18/35] also need to expose privilege state turns out SATP is not enough to figure out what code you're running, because the kernel code is mapped into all userspace's virtual memory areas. You also need the privilege state to be exported. This creates an option to export those bits. --- src/main/scala/vexriscv/plugin/CsrPlugin.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 928fe07af..c2ab72a65 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -82,6 +82,7 @@ case class CsrPluginConfig( deterministicInteruptionEntry : Boolean = false, //Only used for simulatation purposes wfiOutput : Boolean = false, withPrivilegedDebug : Boolean = false, //For the official RISC-V debug spec implementation + exportPrivilege : Boolean = false, var debugTriggers : Int = 2 ){ assert(!ucycleAccess.canWrite) @@ -612,6 +613,9 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep contextSwitching = Bool().setName("contextSwitching") privilege = UInt(2 bits).setName("CsrPlugin_privilege") + if (exportPrivilege) { + val export_priv = out(privilege) + } forceMachineWire = False if(catchIllegalAccess || ecallGen || withEbreak) From ed5babaaab9daac276806c0ca0ba98e5c8c5df62 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 16 Jan 2023 12:39:55 +0100 Subject: [PATCH 19/35] shorter syntax on privilege export --- src/main/scala/vexriscv/plugin/CsrPlugin.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index c2ab72a65..8eeb89834 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -613,9 +613,7 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep contextSwitching = Bool().setName("contextSwitching") privilege = UInt(2 bits).setName("CsrPlugin_privilege") - if (exportPrivilege) { - val export_priv = out(privilege) - } + if (exportPrivilege) out(privilege) forceMachineWire = False if(catchIllegalAccess || ecallGen || withEbreak) From 0aa6e0573d6f69bacbe6006ea801944e19175b88 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 16 Jan 2023 12:43:01 +0100 Subject: [PATCH 20/35] shorter satp export --- src/main/scala/vexriscv/plugin/MmuPlugin.scala | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/src/main/scala/vexriscv/plugin/MmuPlugin.scala index 423b1ce37..f8bd88fc9 100644 --- a/src/main/scala/vexriscv/plugin/MmuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/MmuPlugin.scala @@ -93,18 +93,12 @@ class MmuPlugin(ioRange : UInt => Bool, val sum, mxr, mprv = RegInit(False) mprv clearWhen(csrService.xretAwayFromMachine) } - val satp = if(exportSatp) { - new Area { - val mode = out(RegInit(False)) - val asid = out(Reg(Bits(9 bits))) - // Bottom 20 bits are used in implementation, but top 2 bits are still stored for OS use. - val ppn = out(Reg(UInt(22 bits))) - } - } else { - new Area { - val mode = RegInit(False) - val asid = Reg(Bits(9 bits)) - val ppn = Reg(UInt(22 bits)) + val satp = new Area { + val mode = RegInit(False) + val asid = Reg(Bits(9 bits)) + val ppn = Reg(UInt(22 bits)) // Bottom 20 bits are used in implementation, but top 2 bits are still stored for OS use. + if(exportSatp) { + out(mode, asid, ppn) } } From e83bc5312ec54254fd929c561db4ddfcd6dbe746 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 18 Jan 2023 15:19:33 +0100 Subject: [PATCH 21/35] Fix RVC decompressor don't care #296 --- src/main/scala/vexriscv/plugin/Misc.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/vexriscv/plugin/Misc.scala b/src/main/scala/vexriscv/plugin/Misc.scala index 73d90e695..ce9370f0a 100644 --- a/src/main/scala/vexriscv/plugin/Misc.scala +++ b/src/main/scala/vexriscv/plugin/Misc.scala @@ -12,7 +12,7 @@ object RvcDecompressor{ } def apply(i : Bits, rvf : Boolean, rvd : Boolean): Bits ={ - val ret = Bits(32 bits).assignDontCare() + val ret = B(0, 32 bits) val rch = B"01" ## i(9 downto 7) val rcl = B"01" ## i(4 downto 2) From c57da3c7dc71872251dae877c4fe5de7739d0366 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Sun, 19 Feb 2023 09:50:41 +0100 Subject: [PATCH 22/35] fix too early --- README.md | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/README.md b/README.md index 28bace2ec..c7578c6ff 100644 --- a/README.md +++ b/README.md @@ -1294,42 +1294,6 @@ Write Address 0x04 -> The OpenOCD port is here: -#### EmbeddedRiscvJtag - -VexRiscv also support the official RISC-V debug specification (Thanks Efinix for the funding !). - -To enable it, you need to add the EmbeddedRiscvJtag to the plugin list : - -```scala -new EmbeddedRiscvJtag( - p = DebugTransportModuleParameter( - addressWidth = 7, - version = 1, - idle = 7 - ), - withTunneling = false, - withTap = true -) -``` - -And turn on the withPrivilegedDebug option in the CsrPlugin config. - -Here is an example of openocd tcl script to connect : - -```tcl -# ADD HERE YOUR JTAG ADAPTER SETTINGS - -set _CHIPNAME riscv -jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10002FFF - -set _TARGETNAME $_CHIPNAME.cpu - -target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME - -init -halt -``` - #### YamlPlugin This plugin offers a service to other plugins to generate a useful Yaml file describing the CPU configuration. It contains, for instance, the sequence of instructions required From 366f09a14a4f908efead42b2097043d95fe783de Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Sun, 19 Feb 2023 09:51:18 +0100 Subject: [PATCH 23/35] fix too early --- .../GenFullWithRiscvPrivilegedDebugJtag.scala | 102 ------------------ .../scala/vexriscv/demo/SynthesisBench.scala | 19 +--- 2 files changed, 2 insertions(+), 119 deletions(-) delete mode 100644 src/main/scala/vexriscv/demo/GenFullWithRiscvPrivilegedDebugJtag.scala diff --git a/src/main/scala/vexriscv/demo/GenFullWithRiscvPrivilegedDebugJtag.scala b/src/main/scala/vexriscv/demo/GenFullWithRiscvPrivilegedDebugJtag.scala deleted file mode 100644 index 7dd17dda3..000000000 --- a/src/main/scala/vexriscv/demo/GenFullWithRiscvPrivilegedDebugJtag.scala +++ /dev/null @@ -1,102 +0,0 @@ -package vexriscv.demo - -import spinal.core._ -import spinal.lib.cpu.riscv.debug.DebugTransportModuleParameter -import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} -import vexriscv.plugin._ -import vexriscv.{VexRiscv, VexRiscvConfig, plugin} - -/** - * Created by spinalvm on 15.06.17. - */ -object GenFullWithRiscvPrivilegedDebugJtag extends App{ - def config = VexRiscvConfig( - plugins = List( - new IBusCachedPlugin( - prediction = DYNAMIC, - config = InstructionCacheConfig( - cacheSize = 4096, - bytePerLine =32, - wayCount = 1, - addressWidth = 32, - cpuDataWidth = 32, - memDataWidth = 32, - catchIllegalAccess = true, - catchAccessFault = true, - asyncTagMemory = false, - twoCycleRam = true, - twoCycleCache = true - ), - memoryTranslatorPortConfig = MmuPortConfig( - portTlbSize = 4 - ) - ), - new DBusCachedPlugin( - config = new DataCacheConfig( - cacheSize = 4096, - bytePerLine = 32, - wayCount = 1, - addressWidth = 32, - cpuDataWidth = 32, - memDataWidth = 32, - catchAccessError = true, - catchIllegal = true, - catchUnaligned = true - ), - memoryTranslatorPortConfig = MmuPortConfig( - portTlbSize = 6 - ) - ), - new MmuPlugin( - virtualRange = _(31 downto 28) === 0xC, - ioRange = _(31 downto 28) === 0xF - ), - new DecoderSimplePlugin( - catchIllegalInstruction = true - ), - new RegFilePlugin( - regFileReadyKind = plugin.SYNC, - zeroBoot = false - ), - new IntAluPlugin, - new SrcPlugin( - separatedAddSub = false, - executeInsertion = true - ), - new FullBarrelShifterPlugin, - new HazardSimplePlugin( - bypassExecute = true, - bypassMemory = true, - bypassWriteBack = true, - bypassWriteBackBuffer = true, - pessimisticUseSrc = false, - pessimisticWriteRegFile = false, - pessimisticAddressMatch = false - ), - new MulPlugin, - new DivPlugin, - new CsrPlugin(CsrPluginConfig.small(0x80000020l).copy(withPrivilegedDebug = true)), //withPrivilegedDebug is required - new EmbeddedRiscvJtag( - DebugTransportModuleParameter( - addressWidth = 7, - version = 1, - idle = 7 - ), - debugCd = ClockDomain.current.copy(reset = Bool().setName("debugReset")), - withTap = true, - withTunneling = false - ), - new BranchPlugin( - earlyBranch = false, - catchAddressMisaligned = true - ), - new YamlPlugin("cpu0.yaml") - ) - ) - - def cpu() = new VexRiscv( - config - ) - - SpinalVerilog(cpu()) -} diff --git a/src/main/scala/vexriscv/demo/SynthesisBench.scala b/src/main/scala/vexriscv/demo/SynthesisBench.scala index d0188194d..d13645a13 100644 --- a/src/main/scala/vexriscv/demo/SynthesisBench.scala +++ b/src/main/scala/vexriscv/demo/SynthesisBench.scala @@ -473,22 +473,7 @@ object VexRiscvCustomSynthesisBench { cpu } - - val riscvDebug = new Rtl { - override def getName(): String = "riscvDebug" - override def getRtlPath(): String = "riscvDebug.v" - SpinalVerilog(gen(new CsrPlugin(CsrPluginConfig.smallest(0x80000000l).copy(withPrivilegedDebug = true)), new EmbeddedRiscvJtag( - p = DebugTransportModuleParameter( - addressWidth = 7, - version = 1, - idle = 7 - ), - debugCd = ClockDomain.current.copy(reset = Bool().setName("debugReset")), - withTunneling = false, - withTap = true - )).setDefinitionName(getRtlPath().split("\\.").head)) - } - + val vexDebug = new Rtl { override def getName(): String = "vexDebug" override def getRtlPath(): String = "vexDebug.v" @@ -499,7 +484,7 @@ object VexRiscvCustomSynthesisBench { // val rtls = List(twoStage, twoStageBarell, twoStageMulDiv, twoStageAll, smallestNoCsr, smallest, smallAndProductive, smallAndProductiveWithICache, fullNoMmuNoCache, noCacheNoMmuMaxPerf, fullNoMmuMaxPerf, fullNoMmu, full, linuxBalanced, linuxBalancedSmp) - val rtls = List(riscvDebug, vexDebug) + val rtls = List(vexDebug) // val rtls = List(smallest) val targets = XilinxStdTargets() ++ AlteraStdTargets() ++ IcestormStdTargets().take(1) From 13d66b3ae429318d63e272c70a38f33556a7636d Mon Sep 17 00:00:00 2001 From: Leon Schuermann Date: Fri, 24 Feb 2023 16:08:39 -0500 Subject: [PATCH 24/35] Fetcher: insert FORMAL_MODE encoded from privilegeService Previously, FORMAL_MODE would simply be hard-coded to "11", indicating machine mode. However, that's not necessarily true when using the CsrPlugin, which allows to switch the hart into either User or optional Supervisor mode. Hence we create a FORMAL_MODE insert in the fetch-phase (which is generally when the MPP register can take effect) and generate `rvfi_mode` based on that insert. --- src/main/scala/vexriscv/Services.scala | 14 ++++++++++++++ src/main/scala/vexriscv/VexRiscv.scala | 1 + src/main/scala/vexriscv/plugin/Fetcher.scala | 7 +++++++ src/main/scala/vexriscv/plugin/FormalPlugin.scala | 2 +- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index 140c69bca..f0ef71361 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -50,6 +50,20 @@ trait PrivilegeService{ def isSupervisor() : Bool def isMachine() : Bool def forceMachine() : Unit + + def encodeBits() : Bits = { + val encoded = Bits(2 bits) + + when(this.isUser()) { + encoded := "00" + }.elsewhen(this.isSupervisor()) { + encoded := "01" + }.otherwise { + encoded := "11" + } + + encoded + } } case class PrivilegeServiceDefault() extends PrivilegeService{ diff --git a/src/main/scala/vexriscv/VexRiscv.scala b/src/main/scala/vexriscv/VexRiscv.scala index ed7e37e7c..ebf20089d 100644 --- a/src/main/scala/vexriscv/VexRiscv.scala +++ b/src/main/scala/vexriscv/VexRiscv.scala @@ -95,6 +95,7 @@ case class VexRiscvConfig(){ object FORMAL_MEM_RDATA extends Stageable(Bits(32 bits)) object FORMAL_MEM_WDATA extends Stageable(Bits(32 bits)) object FORMAL_INSTRUCTION extends Stageable(Bits(32 bits)) + object FORMAL_MODE extends Stageable(Bits(2 bits)) object Src1CtrlEnum extends SpinalEnum(binarySequential){ diff --git a/src/main/scala/vexriscv/plugin/Fetcher.scala b/src/main/scala/vexriscv/plugin/Fetcher.scala index f793084e4..061be18cd 100644 --- a/src/main/scala/vexriscv/plugin/Fetcher.scala +++ b/src/main/scala/vexriscv/plugin/Fetcher.scala @@ -415,6 +415,8 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, decode.arbitration.isValid clearWhen(forceNoDecodeCond) }) + val privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault()) + //Formal verification signals generation, miss prediction stuff ? val formal = new Area { val raw = if(compressedGen) decompressor.raw else inputBeforeStage.rsp.inst @@ -437,6 +439,11 @@ abstract class IBusFetcherImpl(val resetVector : BigInt, info.stage.output(FORMAL_PC_NEXT) := info.interface.payload } }) + + // Forward the current CPU "mode" (privilege level) from the fetch + // stage, which is where it can begin to affect the current + // execution (e.g., through PMP checks). + decode.insert(FORMAL_MODE) := privilegeService.encodeBits() } } diff --git a/src/main/scala/vexriscv/plugin/FormalPlugin.scala b/src/main/scala/vexriscv/plugin/FormalPlugin.scala index 2d70ebd8e..5b5e0d58a 100644 --- a/src/main/scala/vexriscv/plugin/FormalPlugin.scala +++ b/src/main/scala/vexriscv/plugin/FormalPlugin.scala @@ -93,7 +93,7 @@ class FormalPlugin extends Plugin[VexRiscv]{ rvfi.trap := False rvfi.halt := False rvfi.intr := False - rvfi.mode := 3 + rvfi.mode := output(FORMAL_MODE) rvfi.ixl := 1 // rvfi.rs1.addr := output(INSTRUCTION)(rs1Range).asUInt // rvfi.rs2.addr := output(INSTRUCTION)(rs2Range).asUInt From 49246e757f242b66e6b691fba28e022c15b94aa2 Mon Sep 17 00:00:00 2001 From: Leon Schuermann Date: Fri, 24 Feb 2023 16:34:06 -0500 Subject: [PATCH 25/35] CsrPlugin: insert FORMAL_HALT := False --- src/main/scala/vexriscv/plugin/CsrPlugin.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 8eeb89834..b59fd8972 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -670,6 +670,10 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep } } + // The CSR plugin will invoke a trap handler on exception, which does not + // count as halt-state by the RVFI spec, and neither do other instructions + // such as `wfi`, etc. Hence statically drive the output: + pipeline.stages.head.insert(FORMAL_HALT) := False case class Xtvec() extends Bundle { val mode = Bits(2 bits) From d8f6f28020eea0692ce4b88716050c517978bf42 Mon Sep 17 00:00:00 2001 From: Andreas Wallner Date: Fri, 7 Apr 2023 18:59:17 +0200 Subject: [PATCH 26/35] Remove sbt-assembly dependency The plugin is not used in the VexRiscV build and causes issues for users since repo.scala-sbt.org seems to be down/sunset/?. See also https://github.com/sbt/sbt/issues/7202 Updating the dependency would also have been an option, but since it's not used removal is easier. --- project/plugins.sbt | 1 - 1 file changed, 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 26ac3e584..e69de29bb 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +0,0 @@ -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10") From 050b4d8c62319715a47f950ca89278cbf0bd2187 Mon Sep 17 00:00:00 2001 From: AdDraw Date: Thu, 15 Jun 2023 22:57:20 +0200 Subject: [PATCH 27/35] Add halfPipe function to DBusSimpleBus --- src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala index dbf660905..7053dcc3c 100644 --- a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala @@ -103,6 +103,13 @@ case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMaste s } + def cmdHalfPipe() : DBusSimpleBus = { + val s = DBusSimpleBus(bigEndian) + s.cmd << this.cmd.halfPipe() + s.rsp <> this.rsp + s + } + def genMask(cmd : DBusSimpleCmd) = { if(bigEndian) cmd.size.mux( @@ -245,7 +252,7 @@ case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMaste } bus } - + def toBmb() : Bmb = { val pipelinedMemoryBusConfig = DBusSimpleBus.getBmbParameter() val bus = Bmb(pipelinedMemoryBusConfig) From 7f647f9d8dffa3ac132f4a25036cc9f421d36726 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 16 Jun 2023 08:47:18 +0200 Subject: [PATCH 28/35] Update DBusSimplePlugin.scala --- src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala index 7053dcc3c..1dfa41f63 100644 --- a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala @@ -106,7 +106,7 @@ case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMaste def cmdHalfPipe() : DBusSimpleBus = { val s = DBusSimpleBus(bigEndian) s.cmd << this.cmd.halfPipe() - s.rsp <> this.rsp + s.rsp >> this.rsp s } From 5860dc2321a9b41737fb094270c067b8acede46c Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 16 Jun 2023 10:07:24 +0100 Subject: [PATCH 29/35] Update DBusSimplePlugin.scala --- src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala index 1dfa41f63..97aa0c13f 100644 --- a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala @@ -106,7 +106,7 @@ case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMaste def cmdHalfPipe() : DBusSimpleBus = { val s = DBusSimpleBus(bigEndian) s.cmd << this.cmd.halfPipe() - s.rsp >> this.rsp + this.rsp := s.rsp s } From 1746af1cfe11d0f75b6d3b4460c7d4a0ff8cf98f Mon Sep 17 00:00:00 2001 From: Charles Papon Date: Tue, 11 Jul 2023 04:13:36 +0800 Subject: [PATCH 30/35] Fix #352 GenCustomInterrupt demo --- .../scala/vexriscv/demo/GenCustomInterrupt.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/vexriscv/demo/GenCustomInterrupt.scala b/src/main/scala/vexriscv/demo/GenCustomInterrupt.scala index d0d9e4852..cffaa1c16 100644 --- a/src/main/scala/vexriscv/demo/GenCustomInterrupt.scala +++ b/src/main/scala/vexriscv/demo/GenCustomInterrupt.scala @@ -11,6 +11,12 @@ object GenCustomInterrupt extends App{ def cpu() = new VexRiscv( config = VexRiscvConfig( plugins = List( + new CsrPlugin( + CsrPluginConfig.smallest.copy( + xtvecModeGen = true, + mtvecAccess = CsrAccess.WRITE_ONLY + ) + ), new UserInterruptPlugin( interruptName = "miaou", code = 20 @@ -19,12 +25,6 @@ object GenCustomInterrupt extends App{ interruptName = "rawrrr", code = 24 ), - new CsrPlugin( - CsrPluginConfig.smallest.copy( - xtvecModeGen = true, - mtvecAccess = CsrAccess.WRITE_ONLY - ) - ), new IBusSimplePlugin( resetVector = 0x80000000l, cmdForkOnSecondStage = false, From badf13be02b077f86da2eedcbb176c92300ca51d Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 10 Aug 2023 09:02:15 +0200 Subject: [PATCH 31/35] SpinalHDL 1.9.2 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 0cdecb8f5..3889ba686 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -val spinalVersion = "1.9.0" +val spinalVersion = "1.9.2" lazy val root = (project in file(".")). settings( From 5ef1bc775fdbe942875dd7906f22aa98e6cffaaf Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 16 Aug 2023 09:59:21 +0200 Subject: [PATCH 32/35] SpinalHDL 1.9.3 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 3889ba686..d749f35b2 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -val spinalVersion = "1.9.2" +val spinalVersion = "1.9.3" lazy val root = (project in file(".")). settings( From 960f8682ea5be8db3c1ef5bc03f9c7d2c4faacb0 Mon Sep 17 00:00:00 2001 From: StaubRobin Date: Mon, 25 Sep 2023 22:15:50 +0200 Subject: [PATCH 33/35] Add missing parameter jtagHeaderIgnoreWidth --- doc/nativeJtag/README.md | 4 ++-- src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/nativeJtag/README.md b/doc/nativeJtag/README.md index 550d8be8b..10c5b7ee9 100644 --- a/doc/nativeJtag/README.md +++ b/doc/nativeJtag/README.md @@ -49,7 +49,7 @@ as given could move with future changes to the file: ``` [254] val jtagCtrl = JtagTapInstructionCtrl() [255] val tap = jtagCtrl.fromXilinxBscane2(userId = 2) -[256] jtagCtrl <> plugin.io.bus.fromJtagInstructionCtrl(ClockDomain(tap.TCK)) +[256] jtagCtrl <> plugin.io.bus.fromJtagInstructionCtrl(ClockDomain(tap.TCK),0) ``` Changing the above lines, removes the Murax SoC’s JTAG ports as pins of the FPGA and inserts the BSCANE2 Xilinx Debug IP to which the JTAG signals are now connected. @@ -84,7 +84,7 @@ in e.g. the path: `project_name.srcs\sources_1\imports\Downloads` [44] wire tesic_tdo; [45] reg soc_tck,soc_tms,soc_tdi; [46] wire soc_tdo; -[47] +[47] [48] always @(*) begin [49] {soc_tck, soc_tms, soc_tdi } = {tck,tms,tdi}; [50] tdo = soc_tdo; diff --git a/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala b/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala index f817fb3be..8a49a27c6 100644 --- a/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala +++ b/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala @@ -168,7 +168,7 @@ object VexRiscvAhbLite3{ // // On Artix FPGA jtag : // val jtagCtrl = JtagTapInstructionCtrl() // val tap = jtagCtrl.fromXilinxBscane2(userId = 1) -// jtagCtrl <> plugin.io.bus.fromJtagInstructionCtrl(ClockDomain(tap.TCK)) +// jtagCtrl <> plugin.io.bus.fromJtagInstructionCtrl(ClockDomain(tap.TCK),0) } case _ => } From a2a60bf6bc1400f3df23368caa43c98188308b00 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 31 Oct 2023 11:05:00 +0100 Subject: [PATCH 34/35] #373 Add GenFullWithTcm demo --- README.md | 1 + .../scala/vexriscv/demo/GenFullWithTcm.scala | 100 ++++++++++++++++++ .../vexriscv/plugin/DBusCachedPlugin.scala | 9 ++ 3 files changed, 110 insertions(+) create mode 100644 src/main/scala/vexriscv/demo/GenFullWithTcm.scala diff --git a/README.md b/README.md index c7578c6ff..c11922f30 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ This repository hosts a RISC-V implementation written in SpinalHDL. Here are som - Linux compatible (SoC : https://github.com/enjoy-digital/linux-on-litex-vexriscv) - Zephyr compatible - [FreeRTOS port](https://github.com/Dolu1990/FreeRTOS-RISCV) +- Support tightly coupled memory on I$ D$ (see GenFullWithTcm) The hardware description of this CPU is done by using a very software oriented approach (without any overhead in the generated hardware). Here is a list of software concepts used: diff --git a/src/main/scala/vexriscv/demo/GenFullWithTcm.scala b/src/main/scala/vexriscv/demo/GenFullWithTcm.scala new file mode 100644 index 000000000..0b7618e1e --- /dev/null +++ b/src/main/scala/vexriscv/demo/GenFullWithTcm.scala @@ -0,0 +1,100 @@ +package vexriscv.demo + +import spinal.core._ +import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} +import vexriscv.plugin._ +import vexriscv.{VexRiscv, VexRiscvConfig, plugin} + +/** + * Both iBusTc and dBusTc assume + * - 1 cycle read latency + * - read_data stay on the port until the next access + * - writes should only occure when dBusTc_enable && dBusTc_write_enable + * - address is in byte, don't care about the 2 LSB + */ +object GenFullWithTcm extends App{ + def config = VexRiscvConfig( + plugins = List( + new IBusCachedPlugin( + prediction = DYNAMIC, + config = InstructionCacheConfig( + cacheSize = 4096, + bytePerLine =32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = true, + catchAccessFault = true, + asyncTagMemory = false, + twoCycleRam = true, + twoCycleCache = true + ), + memoryTranslatorPortConfig = MmuPortConfig( + portTlbSize = 4 + ) + ).newTightlyCoupledPort( + TightlyCoupledPortParameter("iBusTc", a => a(31 downto 28) === 0x2) + ), + new DBusCachedPlugin( + config = new DataCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchAccessError = true, + catchIllegal = true, + catchUnaligned = true + ), + memoryTranslatorPortConfig = MmuPortConfig( + portTlbSize = 6 + ) + ).newTightlyCoupledPort( + TightlyCoupledDataPortParameter("dBusTc", a => a(31 downto 28) === 0x3) + ), + new MmuPlugin( + virtualRange = _(31 downto 28) === 0xC, + ioRange = _(31 downto 28) === 0xF + ), + new DecoderSimplePlugin( + catchIllegalInstruction = true + ), + new RegFilePlugin( + regFileReadyKind = plugin.SYNC, + zeroBoot = false + ), + new IntAluPlugin, + new SrcPlugin( + separatedAddSub = false, + executeInsertion = true + ), + new FullBarrelShifterPlugin, + new HazardSimplePlugin( + bypassExecute = true, + bypassMemory = true, + bypassWriteBack = true, + bypassWriteBackBuffer = true, + pessimisticUseSrc = false, + pessimisticWriteRegFile = false, + pessimisticAddressMatch = false + ), + new MulPlugin, + new DivPlugin, + new CsrPlugin(CsrPluginConfig.small(0x80000020l)), + new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))), + new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = true + ), + new YamlPlugin("cpu0.yaml") + ) + ) + + def cpu() = new VexRiscv( + config + ) + + SpinalVerilog(cpu()) +} diff --git a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala index 593eb9115..e32ff96d7 100644 --- a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala @@ -87,6 +87,12 @@ class DBusCachedPlugin(val config : DataCacheConfig, val tightlyCoupledPorts = ArrayBuffer[TightlyCoupledDataPort]() def tightlyGen = tightlyCoupledPorts.nonEmpty + def newTightlyCoupledPort(p: TightlyCoupledDataPortParameter) = { + val port = TightlyCoupledDataPort(p, null) + tightlyCoupledPorts += port + this + } + def newTightlyCoupledPort(mapping : UInt => Bool) = { val port = TightlyCoupledDataPort(TightlyCoupledDataPortParameter(null, mapping), TightlyCoupledDataBus()) tightlyCoupledPorts += port @@ -173,6 +179,9 @@ class DBusCachedPlugin(val config : DataCacheConfig, import Riscv._ import pipeline.config._ + + tightlyCoupledPorts.filter(_.bus == null).foreach(p => p.bus = master(TightlyCoupledDataBus()).setName(p.p.name)) + dBus = master(DataCacheMemBus(this.config)).setName("dBus") val decoderService = pipeline.service(classOf[DecoderService]) From beeec94344bd0caede9b0d91aee3e198d8b6f5b3 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 2 Nov 2023 12:31:05 +0100 Subject: [PATCH 35/35] Add GenFullWithTcmIntegrated example --- README.md | 2 +- .../demo/GenFullWithTcmIntegrated.scala | 97 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/vexriscv/demo/GenFullWithTcmIntegrated.scala diff --git a/README.md b/README.md index 30c814edf..165341fc1 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ This repository hosts a RISC-V implementation written in SpinalHDL. Here are som - Linux compatible (SoC : https://github.com/enjoy-digital/linux-on-litex-vexriscv) - Zephyr compatible - [FreeRTOS port](https://github.com/Dolu1990/FreeRTOS-RISCV) -- Support tightly coupled memory on I$ D$ (see GenFullWithTcm) +- Support tightly coupled memory on I$ D$ (see GenFullWithTcm / GenFullWithTcmIntegrated) The hardware description of this CPU is done by using a very software oriented approach (without any overhead in the generated hardware). Here is a list of software concepts used: diff --git a/src/main/scala/vexriscv/demo/GenFullWithTcmIntegrated.scala b/src/main/scala/vexriscv/demo/GenFullWithTcmIntegrated.scala new file mode 100644 index 000000000..4e68b5f6a --- /dev/null +++ b/src/main/scala/vexriscv/demo/GenFullWithTcmIntegrated.scala @@ -0,0 +1,97 @@ +package vexriscv.demo + +import spinal.core._ +import spinal.lib.bus.misc.SizeMapping +import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} +import vexriscv.plugin._ +import vexriscv.{VexRiscv, VexRiscvConfig, plugin} + +/** + * this example integrate the tightly coupled memory directly inside VexRiscv + * by using the IBusDBusCachedTightlyCoupledRam plugin + */ +object GenFullWithTcmIntegrated extends App{ + def config = VexRiscvConfig( + plugins = List( + new IBusDBusCachedTightlyCoupledRam( + mapping = SizeMapping(0x20000000, 0x1000) + ), + new IBusCachedPlugin( + prediction = DYNAMIC, + config = InstructionCacheConfig( + cacheSize = 4096, + bytePerLine =32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = true, + catchAccessFault = true, + asyncTagMemory = false, + twoCycleRam = true, + twoCycleCache = true + ), + memoryTranslatorPortConfig = MmuPortConfig( + portTlbSize = 4 + ) + ), + new DBusCachedPlugin( + config = new DataCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchAccessError = true, + catchIllegal = true, + catchUnaligned = true + ), + memoryTranslatorPortConfig = MmuPortConfig( + portTlbSize = 6 + ) + ), + new MmuPlugin( + virtualRange = _(31 downto 28) === 0xC, + ioRange = _(31 downto 28) === 0xF + ), + new DecoderSimplePlugin( + catchIllegalInstruction = true + ), + new RegFilePlugin( + regFileReadyKind = plugin.SYNC, + zeroBoot = false + ), + new IntAluPlugin, + new SrcPlugin( + separatedAddSub = false, + executeInsertion = true + ), + new FullBarrelShifterPlugin, + new HazardSimplePlugin( + bypassExecute = true, + bypassMemory = true, + bypassWriteBack = true, + bypassWriteBackBuffer = true, + pessimisticUseSrc = false, + pessimisticWriteRegFile = false, + pessimisticAddressMatch = false + ), + new MulPlugin, + new DivPlugin, + new CsrPlugin(CsrPluginConfig.small(0x80000020l)), + new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))), + new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = true + ), + new YamlPlugin("cpu0.yaml") + ) + ) + + def cpu() = new VexRiscv( + config + ) + + SpinalVerilog(cpu()) +}