Building the Beta

When entering numeric values in the answer fields, you can use integers (1000, 0x3E8, 0b1111101000), floating-point numbers (1000.0), scientific notation (1e3), engineering scale factors (1K), or numeric expressions (3*300 + 100).

Useful links:

Problem 1. Beta Control ROM

For each of the following potential additions to the Beta instruction set, fill in the table with the control signal settings needed to execute these instructions on an unpipelined Beta. Assume the standard Beta datapath and control signals. Please choose "don't care" if the value of control signal doesn't matter when executing the instruction.

  1. Swap register contents with memory location
    Usage:     MSWP (Ra, literal, Rc)
    Operation: PC <- PC + 4
               EA <- Reg[Ra] + SEXT(literal)
               tmp <- Mem[EA]
               Mem[EA] <- Reg[Rc]
               Reg[Rc] <- tmp
    don't care adder unit: A + B adder unit: A - B boole unit: A AND B boole unit: A OR B boole unit: A XOR B boole unit: NOT A boole unit: select A operand boole unit: select B operand shift unit: SHL shift unit: SHR shift unit: SRA compare unit: CMPEQ compare unit: CMPLT compare unit: CMPLE don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 2 3 4 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 2 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1

  2. Move if zero
    Usage:     MVZ (Ra, Rb, Rc)
    Operation: PC <- PC + 4
               if Reg[Ra] == 0 then Reg[Rc] <- Reg[Rb]
    don't care adder unit: A + B adder unit: A - B boole unit: A AND B boole unit: A OR B boole unit: A XOR B boole unit: NOT A boole unit: select A operand boole unit: select B operand shift unit: SHL shift unit: SHR shift unit: SRA compare unit: CMPEQ compare unit: CMPLT compare unit: CMPLE don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 2 3 4 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 2 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1

  3. Move constant if zero
    Usage:     MVZ (Ra, literal, Rc)
    Operation: PC <- PC + 4
               if Reg[Ra] == 0 then Reg[Rc] <- SEXT(literal)
    don't care adder unit: A + B adder unit: A - B boole unit: A AND B boole unit: A OR B boole unit: A XOR B boole unit: NOT A boole unit: select A operand boole unit: select B operand shift unit: SHL shift unit: SHR shift unit: SRA compare unit: CMPEQ compare unit: CMPLT compare unit: CMPLE don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 2 3 4 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 2 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1

  4. Load indexed
    Usage:     LDX (Ra, Rb, Rc)
    Operation: PC <- PC + 4
               Reg[Rc] <- Mem[Reg[Ra] + Reg[Rb]]
    don't care adder unit: A + B adder unit: A - B boole unit: A AND B boole unit: A OR B boole unit: A XOR B boole unit: NOT A boole unit: select A operand boole unit: select B operand shift unit: SHL shift unit: SHR shift unit: SRA compare unit: CMPEQ compare unit: CMPLT compare unit: CMPLE don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 2 3 4 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 2 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1

Problem 2. New ALU!

Ben Bitdiddle has proposed changing the adder unit of the Beta ALU as shown in the following diagram. His goal is to use the adder unit to compute more than just "A+B" and "A-B". The changes include one additional inverter and three additional 2-input NAND gates for each bit of the adder unit. The "x32" appearing inside the gate icons indicates that those gates are replicated 32 times to handle all 32 bits of incoming data.

  1. For each of the eight possible values of the three control bits OP[2:0] indicate what operation the revised adder unit will perform. A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above A B A+B A-B A+1 B+1 A-1 B-1 A+B+1 A+B-1 A-B+1 A-B-1 none of the above

  2. To show off the capabilities of his new adder unit, Ben proposes adding a LOOP instruction which combines branching and decrementing in a single instruction. Ben's theory is that the SUB/BNE instructions that appear at the end of a FOR-loop can be combined into a single LOOP instruction. Here's his definition for LOOP:
    Usage:     LOOP(Ra, label, Rc)
    Operation: literal = ((OFFSET(label) - OFFSET(current inst))/4) - 1
               PC <- PC + 4
               EA <- PC + 4*SEXT(literal)
               tmp <- Reg[Ra]
               Reg[Rc] <- Reg[Ra] - 1
               if tmp != 0 then PC <- EA 
    The LOOP instruction behaves like a BNE in the sense that it branches if Reg[Ra] is not zero. But instead of saving the PC of the following instruction in Rc, Reg[Ra]-1 is stored in Rc instead. The destination of the branch is determined as for all branches: the literal field of the instruction is treated as a word offset, so it is sign-extended, multiplied by four and added to PC+4 to produce a new value for the PC. Usually Ra and Rc specify the same register.

    Consider the following instruction sequence:

    loop: ADD(R1,R2,R3)
          LOOP(R4,loop,R4)
          ...
    Suppose R4 is initialized to 8 and then the two-instruction sequence shown above is executed.

  3. Fill in the table with the control signal settings needed to execute the LOOP instruction on an unpipelined Beta that includes Ben's new adder unit. Please choose "don't care" if the value of control signal doesn't matter when executing LOOP.; don't care adder unit: A + B adder unit: A - B adder unit: A adder unit: A + 1 adder unit: A - 1 adder unit: A + B + 1 adder unit: A - B - 1 boole unit: A AND B boole unit: A OR B boole unit: A XOR B boole unit: NOT A boole unit: select A operand boole unit: select B operand shift unit: SHL shift unit: SHR shift unit: SRA compare unit: CMPEQ compare unit: CMPLT compare unit: CMPLE don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 2 3 4 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1 don't care 0 1 2 don't care 0 1 if Reg[Ra]==0 then 1 else 0 if Reg[Ra]==0 then 0 else 1

Problem 3. Design Problem: Building the Beta

See the instructions below.

Use the Jade instance below to enter your design. To complete this design problem, select the /beta/test module and click in the Jade toolbar and the built-in tester will either report any discrepencies between the expected and actual outputs, or, if your design is correct, it will record the test passed. { "shared_modules": ["../exercises/alu/alu_modules", "../exercises/beta/beta_modules"], "hierarchical": "true", "required_tests": ["327ee733491d310e278d231ad43f4abc"], "parts": ["/gates/.*","/alu/.*","/beta/.*","memory","/user/.*"], "tools": ["check"], "editors": ["schematic","icon","test"], "edit": "/beta/test" }

The goal of this lab is to complete the design of the Beta. We'll start by working on the three major subassemblies called out in the block diagram shown below: register file, control logic, and program counter. Once those modules pass their tests, we'll work on the Beta itself, adding the ALU design from Lab 3 and the various muxes that select how the data is routed between the units.

There are design notes below suggesting how to go about the design of each of the sub-modules.

Your first step is to copy over the ALU modules you designed in Lab 3 so that we can use your ALU in the Beta. Here's what you need to do:

  1. In another browser window open up the "32-bit ALU" exercise and scroll down to the Jade window in Problem 1. In the module toolbar at the top of the Jade window, click on to copy your /alu modules onto the module clipboard.

  2. Returning to this window, in the module toolbar below, click on , which will pop up a window to let you select which modules to copy into the "Building the Beta" workspace. Select all the /alu modules and click "OK", which will add the selected modules to your parts bin.

  3. Double-check that everything's okay by editing /alu/alu (use the Module: selection box in the module toolbar) and re-running the tests. Hopefully the tests will pass and you're good to go!

Here are some step-by-step design notes for tackling the Beta design.

Register File (REGFILE)

Using the module toolbar, select the /beta/regfile module to edit. The icon for the REGFILE module is shown on the right. The module's inputs are on the left, top, and right; its outputs are on the bottom. The block diagram for the module's internal circuitry is shown on the block diagram above.

The heart of the REGFILE module is a 3-port memory, built using the MEM component (found in the toolbar above the schematic diagram). Start your design by dragging an instance of the MEM component into your schematic then editing its properties to set the number of ports to 3, the width of the address to 5 (specifying 32 locations) and the width of the data to 32. Correctly configured, the MEM component icon should look like the one shown on the right below. Note that the CLK inputs are labeled with the triangle symbol we use to indicate an input triggered by a 0→1 transition.

Two of the ports are used from reading values from the memory. A read port is configured by setting the port's OE input to 1 (connect it to a wire labeled 1'1) and the WE and CLK inputs to 0 (connect them to wires labeled 0'1).

The third port is used for writing values into the memory. A write port is configured by setting the OE to 0 and connecting the WE and CLK to the appropriate signals. A write will occur on the rising edge of the CLK input if the WE input is a 1.

Now add the RA2SEL and WASEL address muxes to the select the addresses for the second read port and the write port. The WASEL mux input labeled XP in the block diagram is indicating the address of the XP register, i.e., register 30.

Hint: Remember that the default width of a wire is 1. The outputs of the RA2SEL and WASEL muxes should each be 5 bits wide, the width of a register address. So you'll need to set the widths of the wires connecting the MUX outputs to the memory, either by giving the wires names that implicitly specify a width, e.g., ADDR[4:0], or by setting the wire's width property to 5.

Remember to set width of each wire if the width of the wire is greater than 1. Jade will "transmit" the width information along connected wire segments, so you'll only need to set the width somewhere along the wire's run.

Note that the memory component doesn't know that location 31 of the register file should always read as zero, so you'll have to add additional logic around the memory that makes this happen. You can use muxes or ANDs to force the register data for each read port to "0" when the port address is 0b11111 (i.e., R31). Note that the address for the second read port comes from the output of the RA2SEL mux, so that's the value that needs to be tested for that port.

When you're ready to test your circuit, click the green checkmark to run the provided tests that verify your circuit has the functionality described above. If an error is reported, look at the TEST aspect and read the comments associated with that particular test cycle.

Now would be a good time to save your work :)

Hint: What do I do when the tests fail?

The failure notification will tell you which signal failed verification and the simulated time at which the mismatch between the actual value and expected occurred. The tests are actually a sequence of 100ns testing cycles and the reported time will be at the end of one of the cycles when the output values are checked for correctness.

Move your mouse over the plot of the appropriate signal waveform until the vertical time cursor is approximately at the failure time. Then double-click to zoom in on the plots around that particular time; zoom in enough so that all the signals for that testing cycle are readable. Now you can figure out what the circuit was being asked to do for that particular test and, hopefully, deduce why your circuit is producing an incorrect output.

Control Logic (CTL)

Using the module toolbar, select the /beta/ctl module to edit. The icon for the PC module is shown on the right. The module's inputs are on the left, its outputs are on the right.

The heart of the CTL module is the control ROM, a 64-entry lookup table addressed by the 6-bit opcode field of the instruction OP[5:0], which outputs the appropriate values for the 18 control signals. The ROM can be constructed using MEM component configured to have one read port and with its Contents property initialized with the appropriate values (see the Control logic table in the Unpipelined Beta diagram). That diagram also has a table of ALUFN values that will be useful when filling in the ALUFN[5:0] values for each instruction.

The contents property should be a list of 64 18-bit values. You can use extra whitespace and the standard "//" and "/* ... */" comment conventions to make contents more readable. "+" and "_" can be used to separate subfields and will be ignored by the contents parser. You can use "?" to indicate a "don't care" value for a particular digit in a numeric value; it will be replaced by a 0 when Jade builds the ROM contents.

To get you started, we've provided a control ROM contents file which you can copy-and-paste into the contents property of the MEM component. You should then edit the control signal values specified for each of the 64 opcodes so that the ROM will generate the signals necessary for the Beta to correctly execute instructions with that particular opcode.

Hint: When editing a property you can enlarge the input field by clicking and dragging on the lower right-hand corner of the input field.

In this file the control signals have been set as if the corresponding opcode was an illegal instruction, i.e., using the ILLOP column of the Control logic table. The contents are formatted so that ALUFN[5] is the most-significant bit of the 18-bit output and WERF is the least-significant bit — see the comments in the file for the exact order of the signals in each binary value. There's an 18-bit binary value specifying the control signals for each of the 64 opcodes, listed in opcode order. There's a comment at the end of each line that indicates the opcode number and the corresponding Beta opcode, if any. Here's a small excerpt from the middle of the file:

// alufn[5:0]
// asel, bsel
// moe, mwr
// pcsel[2:0]
// ra2sel
// wasel, wdsel[1:0], werf
0b??????_??_?0_011_?_1001  // 0b100000 ADD
0b??????_??_?0_011_?_1001  // 0b100001 SUB
0b??????_??_?0_011_?_1001  // 0b100010 MUL
0b??????_??_?0_011_?_1001  // 0b100011 DIV
0b??????_??_?0_011_?_1001  // 0b100100 CMPEQ
0b??????_??_?0_011_?_1001  // 0b100101 CMPLT
0b??????_??_?0_011_?_1001  // 0b100110 CMPLE
0b??????_??_?0_011_?_1001  // 0b100111
The first 6 bits on a line specify the values for ALUFN[5:0], the next 2 bits specify the values for ASEL and BSEL, and so on.

Some of the outputs of the control ROM will have to be modified by external logic before connecting the appropriate output of the CTL module:

When you've completed the schematic for the CTL module, click on the green checkmark in the toolbar to run the provided tests. The tests ensure that all the functionlity described above is implemented correctly.

Program Counter (PC)

Using the module toolbar, select the /beta/pc module to edit. The icon for the PC module is shown on the right. The module's inputs are on the left, its outputs are on the right. The block diagram for the module's internal circuitry is shown on the block diagram above.

The heart of the PC module is the 32-bit register that holds the current value of the program counter, which is the address in main memory of the instruction to be executed in the current clock cycle, PC[31:0]. The high-order bit of the register, PC[31], is used as the supervisor bit. When the supervisor bit is 0, the Beta is in user mode, executing programs normally with interrupts enabled. When the supervisor bit is 1, the Beta is in supervisor mode (sometimes called kernel mode), executing kernel code with interrupts disabled.

The PC register can be built using the DREG component from the parts library. You should include hardware for the bottom two bits of the PC even though they are always 0; this will make debugging traces easier to interpret.

The 5-input 32-bit PCSEL multiplexer selects the value to be loaded into the PC register at next rising edge of the clock. Since the standard cell library doesn't have any 5-input multiplexers, you'll have to construct the logic that selects the next PC using a combination of other components. Remember to add a way to set the PC to 0x80000000 on reset. We'll use the RESET signal to force the PC to 0x80000000 during the first cycle of operation. We'll describe each of the PCSEL input values in more detail below.

The PC module contains two 32-bit adders, which you can build as ripple-carry adders using full-adder module you used in the Lab 3 in the ARITH module of the ALU.

Here are some more details about the five inputs to the PCSEL mux.

When you've completed the schematic for the PC module, click on the green checkmark in the toolbar to run the provided tests. The tests ensure that all the functionlity described above is implemented correctly.

Everything else

Now it's time to put it all together: using the module toolbar, select the /beta/beta module to edit. This implementation of the Beta subcircuit has the following terminals:

clk input clock (from test circuitry): a 10MHz square wave creating a 100ns clock period.
reset input reset (from test circuitry): set by the test circuitry to 1 until after the first rising edge of clk, then set to 0 to start the Beta running.
irq input interrupt request (from test circuitry): set by the test circuitry to 1 to interrupt execution of a running user-mode program (i.e. programs where PC[31] = 0), saving PC+4 in Reg[XP], and setting the PC to 0x80000008.
ia[31:0] outputs instruction address (PC[31:0] from the PC module): address of the next instruction to be executed. This sent to the first read port of main memory.
id[31:0] inputs instruction data (from test circuitry). After the appropriate propagation delay, the main memory will drive these signals with the contents of the memory location specified by ia[31:0].
ma[31:0] outputs memory data address (from ALU): address of data location in main memory to be read or written. This is sent to second read port of main memory.
mrd[31:0] inputs memory read data (from test circuitry): if moe is 1, the main memory will drive these signals with the contents of the memory location specified by ma[31:0].
moe output memory read data output enable (from control logic): should be set to 1 when the Beta want to read the contents of the memory location specified by ma[31:0].
mwd[31:0] outputs memory write data (from register file): if wr is 1, this is the data that will be written into memory location ma[31:0] at the end of the current cycle.
mwr output memory write enable (from control logic): Set to 1 when the Beta wants to store into the memory location specified by ma[31:0] at the end of the current cycle. NOTE: this signal should always have a valid logic value at the rising edge of CLK otherwise the contents of the memory will be erased. You'll need to take care in designing the logic that generates this signal — see the CTL section above for details.

Next step: add the appropriate components to the Beta schematic and create connections shown in the block diagram above. Note that the Instruction and Data memories (shown in grey) will be provided by the test circuitry and are not part of your Beta design. You'll need instances of your PC, CTL, REGFILE and ALU modules along with the following:

When you're ready to test your design, select the /beta/test module. This module contains an instance of your Beta module along with a 3-port main memory which has been initialized with the binary code for a test program that checks out your Beta's functionality. Click on the green checkmark to run the test, which will execute for 433 cycles, verifying the correct values on the Beta's outputs each cycle. The checks are made just before each rising clock edge, i.e., after the current instruction has been fetched and executed, but just before the result is written into the register file. Since ma[31:0] is actually just the output of the ALU, we're able to verify all the OP- and OPC-class instructions. If you get a verification error, check the instruction that has just finished executing at the time reported in the error message — the Beta has executed that instruction incorrectly for some reason.

Almost nobody's design executes the checkoff program correctly the first time — it will take some effort to debug your design, but stick with it. If you're stuck, get help from your fellow students or the course staff. When it works, congratulations! The design of a complete CPU at the gate level is a significant accomplishment. Of course, now the fun is just beginning — there are undoubtedly many ways you can make improvements, both large and small.

Good luck! By finishing you'll earn your very own "Beta Inside" sticker ☺

Appendix: Test program

The BSim instance below shows the Beta program used to test the functionality of your Beta implementation. You may find it useful to compare the cycle-by-cycle operation of your circuit with the cycle-by-cycle execution of the test program in the BSim simulator. Note that any changes you make below (e.g., adding breakpoints) will not be saved when leaving this window.

{ "initial_state": { "beta.uasm": "url:../exercises/beta.uasm" }, "state": {"Beta": "url:../exercises/beta/beta_test.uasm"} }

cd23d89a1c4843216f7a1e113c65c8e10a997b190a65281c70e51373257988f07d9a4c3962875879b34ac57be94901e02d5af9ae57ea4dcca3165ddd52baad9fb9f9c40c9ee306d69f0213d58724b0b317244bf3050b0b28d6165d163e20970aa8a5d796bcd517bb889bcc5fce40a419964363ed3a017cadabd9f42fe3727a1bc11249f1b72ad38a12c6a8ea311966849a965d67f45dce949998c564acb6f00497b95265d42f87c9c19bc27c4b8c879078851baaee4654be64b3b2b8795fc16ea0da57e503ee2442537be573d93781bf259cfb9a5902e88142b2f889c5e634f730b0a80780fbe12146c808eba686a5137544f440508b3ae9eca6a086ded50d1981431fb034b6d52a949f26b8b72ad38a12c6a8ea40d26b0641af6bae6408b6055b76fbdbffbfb3085f791c6435bc82112b2cb7b1b0894afe63395aa66cba8085b079c00f42272052b1a246a12f758519144302cd357af8769eff9f6786f54fd43f4f1f89d1fad7e4ffbf3ebee05f33a9d28edae420ea0ca2f77649ffc4e725629836759bbb8bf228346e09e9dc0d314d60454f5330727fba6bfb09a3f9f13ebce95bbc5757bf75fe6584e16dd2f9a7b1454761037e5c01a7f7f06e1e8739b02eeec3a624b1da3e6926f54f9f11ea5613b400646bded346607d59f84bbb898536ea07d0ab0b503592e0ab5910e3727a1bc11249f1625d1ea89fc4037fdea45cc796dfe00885bde37347381872dcc040e0b272739d39aca9e7ba3b83bbd2ae0e8cbd36990df4f9e9a74b91f771b0894afe63395aa627b9f2d21cb1ac394d9bd221574a95838dbc073e0300dcafc8fd6679ab120330cbc5869236b319376c96d946c53cec1183c3b41e5e03fe2d05aaa2370718e5f48447f14131155cc30276a61296c16adb300c332023aa511177bd481e363d83a7ce40a419964363ed74dcf7ca69e34b9bab6414c2a456a9ab7d8286cafb5e66018503fcda4b12ac04b40f9d00c8846a2e5902e88142b2f889e48bf848e2f7c099f70b8457eba48337540e6683b29df88a6e3a2b78ed2d556e624789640a0976ab41795a9160d44568476b2e70200c131158b01cfe63fa4d0a2a926c5524e1d7bc435d630deb4a0f648b30c4674cdd968b9b06dbf4eb50367167c8f6eab4d3a582cd15fc8f5a9f8a9e352d5e51b02183ae9f5268006e6e02dddf34ebb521e6a9322c7539111bcc0e5e4f869f42e7e194daee3fad899b1030f3f46aa0073eb87086