Binary Logic (UPDATE: 4-Bit CPU simulation, no combincators!)

Circuit-free solutions of basic factory-design to achieve optimal item-throughput.
Involving: Belts (balancers, crossings), Inserters, Chests, Furnaces, Assembling Devices ...
Optimized production chains. Compact design.
Please provide blueprints!
Forum rules
Circuit-free solutions of basic factory-design to achieve optimal item-throughput
Patric20878
Fast Inserter
Fast Inserter
Posts: 160
Joined: Tue Feb 24, 2015 4:53 pm
Contact:

Re: Binary Logic (UPDATE: 4-Bit CPU simulation)

Post by Patric20878 »

Ah, then it's just zaubara then. Wonder if he was the RDF.
Tekkit Classic expert and admin of the Tekkit Classic Wikia specializing in factory and frame gunship engineering, creator of the Optimized Steam Engine Setup, and a huge fan of Touhou. My TC designs may be found at https://imgur.com/a/IT0Ya.

zaubara
Burner Inserter
Burner Inserter
Posts: 14
Joined: Fri Jan 02, 2015 11:12 am
Contact:

Re: Binary Logic (UPDATE: 4-Bit CPU simulation)

Post by zaubara »

hey guys,

i am sorry for my late answer, i had not much time/spirit for factorio lately, still I've got a bit of progress.

I've tried to figure out some ways to reduce the amount of cells, I screwed the same way like you did :) I was imaging a design quite like yours, but I also forgot the fact that inserters still move only 1 type of item at once (which can be accomodated by using paralell inserters/chests moving in and out). I still have to do some experiments with it... My obvious thought was, when I put in the bits manually into the ROM, to keep this process as simple as possible. But that doesn't matter anymore if the ROM blueprint is auto-generated itself, great thing!

How do you input the circuit you exporting the blueprint from? Some kind of RTL? Why not make an HDL compiler and generate the complete CPU? :) I am curious to see some details of it! I know a little bit Java (supported by knowing C and general OOP-knowledge), so fiddling around in existing code should be possible ;)
I've once modified a Java AVR simulator a bit (Avrora, as part of a µC software unit testing still-WIP project)...
NotABiter wrote:BTW, I'm currently working on a "hi-res" display (Nx2 "pixels" in each tile - I haven't actually measured what N is yet but is probably in the 2.x range -- it's just the density at which items are placed on belts -- it was inspired by your 7-segment display) along with "driver logic" and a "character generator ROM", and it's the ROM that I'm trying to create and import first as a blueprint. The ROM should hold 768 bytes (96 character definitions) of "full-speed" data in just 96x72 tiles, and support reading a full 64 bits (one 8x8 pixel character) worth of data every "cycle" (a bit longer than one fast-inserter time). (I figured out how to store 8 bits of full-speed ROM data using just 4 chests and 4 inserters + poles/wires.)
So basically a printer which spits out 2 different items according to the ROM data? Nice!
8 bits in 4 chests, ok, but 4 inserters? How?
Patric20878 wrote:Hey guys, I'm interested in knowing: as you've (both you?) built CPU's in Minecraft, were you ever part of The Redstone Foundation server long ago?
Yes, but just a small time, then I've run a small server on my own for some friends and me. I never actively contributed to the RDF... I haven't finished my CPU build and somehow I lost interest in playing Minecraft when all this unfinished buggy adventure stuff came in and most redstoners were building farms and guns, but if you're interested, you can see some of my builds here

btt, I've redesigned the instruction set with the 13th bit:
NOP, ADD, ADDI, ADDC, SUB, SUBI, SUBC, AND, ANDI, OR, ORI, XOR, XORI, SHL, SHLC, ROL, SHR, SHRC, ROR, ASR, CMP, CMPI, CPC, JMP, JMPR, JZ, JZR, JNZ, JNZR, JCS, JCSR, JLS, JLSR, JGT, JGTR, CALL, RET, LDI, MOV, IN, INR, OUT, OUTR, OUTI; NOT as pseudo-instruction (-I instructions: immediate (constant), -C instructions: use carry, -R instructions: relative jump)
I am not through yet updating the simulation, I'll update the download when I have it working, same for the assembler (btw, the .org directive does exactly what you've described).
But I am not quite sure what you mean by keeping the Z flag over a synthesized operation (ex 8 bit addition), to be more precise, what impact on the design it has, I can see the need for this... Somehow i have to reset the Z flag prior to such an operation (or vice versa keep it during it), is it a common way to have the Carry-less instructions (ADD, SUB, ...) also resetting the Z flag (to not use an extra operation dedicated to reset the flag)?

I remember the SPARCs beeing the number crunchers in the upper segment workstations back in the days (I am bit too late for that time), awkward to emulate such bit counting instructions, bad designer's move... Is it that what happened to them, that I say "back in the days"? :D

The belt design is a very interesting thing, I'll have to dig through it some time :)

NotABiter
Fast Inserter
Fast Inserter
Posts: 124
Joined: Fri Nov 14, 2014 9:05 am
Contact:

Re: Binary Logic (UPDATE: 4-Bit CPU simulation)

Post by NotABiter »

zaubara wrote:How do you input the circuit you exporting the blueprint from?
Here's a simple bit of test code that shows the general idea (with lots of comments added):

Code: Select all

   final Vector<Entity> entities = new Vector<Entity>();

   final SmartInserter inserter1 = new SmartInserter(); // make a new smart inserter
   inserter1.dir = UP; // grab from next tile down, put to next tile up
   inserter1.set_tile_pos(0, 2); // this position is relative to mouse cursor when placing blue print in game
   entities.add(inserter1); // add smart-inserter to list of entities that will be generated
   inserter1.add_filter(COAL); // only grab coal...
   inserter1.add_filter(STONE); // ...or stone

   final SmallElectricPole pole1 = new SmallElectricPole(); // make a new electric pole
   pole1.set_tile_pos(2, 1);
   entities.add(pole1);

   connect(RED, inserter1, pole1); // use red wire to connect the inserter to the electric pole
   inserter1.add_condition(RED, IRON_ORE, GREATER_THAN, 100); // set red wire condition on the smart inserter
   inserter1.add_condition(LOGISTICS, STONE, LESS_THAN, 47);

   final RequesterChest rc = new RequesterChest(); // make a new requester chest
   rc.set_tile_pos(0, 0);
   entities.add(rc);
   rc.add_request_filter(COPPER_PLATE, 99); // set it to request 99 copper...
   rc.add_request_filter(IRON_ORE, 100);    // ...and 100 iron ore

   check(entities); // run the constraint checker
   out(entities);   // generate the blueprint, sends the blueprint string to stdout
I just run the program, redirecting stdout to a file. I then open that file in an editor and copy it's contents. Then in the game I use the blueprint string mod to import the blueprint. (Have empty blueprint in toolbelt, paste the string into the blueprint string mod's text box, click on "Load String". Voila - blueprint in toolbelt is no longer empty.)

The constraint checker throws an exception if it detects a problem, like if two poles were 'connect'ed with the connect function but they're too far apart.

The power of course is that you can use Java to create Factorio entities in loops and using various bits of Java math/logic to decide what to create where, as can be seen in this test code that creates every inserter type at every orientation (so 16 inserters):

Code: Select all

   final Vector<Entity> entities = new Vector<Entity>();
   for (int y = 0; y < 4; y++) {
      for (int x = 0; x < 4; x++) {
         Entity e;
         switch (y) {
            case 0: e = new      BasicInserter(); break;
            case 1: e = new       FastInserter(); break;
            case 2: e = new LongHandedInserter(); break;
            case 3: e = new      SmartInserter(); break;
            default:
               throw null;
         }
         switch (x) {
            case 0: e.dir =    UP; break;
            case 1: e.dir = RIGHT; break;
            case 2: e.dir =  DOWN; break;
            case 3: e.dir =  LEFT; break;
            default:
               throw null;
         }
         e.set_tile_pos(x * 2, y * 2);
         entities.add(e);
      }
   }
   check(entities);
   out(entities);
To export to XFfig I just replace the "out" function with the "export" function (though export does not handle very much stuff yet).

A real example -- my character generator ROM:

Code: Select all

   // 0 is encoded as 4 rather than 0 so completion of a read can always be detected
   //
   static int encode_bits(int value) {
      if (value == 0) {
         return 4;
      } else if (value >= 1 && value <= 3) {
         return value;
      }
      throw new RuntimeException();
   }

   final static String[] item_encoding = new String[]{
      IRON_ORE, // 0
      IRON_PLATE, // 1
      IRON_GEAR_WHEEL, // 2
      IRON_STICK, // 3

      COPPER_ORE, // 4
      COPPER_PLATE, // 5
      COPPER_CABLE, // 6
      GREEN_WIRE, // 7
      RED_WIRE, // 8

      BASIC_BELT, // 9
      BASIC_UNDERGROUND, // 10
      BASIC_SPLITTER, // 11

      RAW_WOOD, // 12
      WOOD, // 13

      STONE, // 14
      STONE_BRICK, // 15

      COAL, // 16
      STEEL_PLATE, // 17

      ELECTRONIC_CIRCUIT, // 18
      REGULAR_MAGAZINE, // 19

      BASIC_INSERTER, // 20
      FAST_INSERTER, // 21
      LONG_HANDED_INSERTER, // 22
      SMART_INSERTER, // 23

      ACTIVE_PROVIDER_CHEST, // 24
      IRON_CHEST, // 25
      PASSIVE_PROVIDER_CHEST, // 26
      REQUESTER_CHEST, // 27
      SMART_CHEST, // 28
      STEEL_CHEST, // 29
      STORAGE_CHEST, // 30
      WOODEN_CHEST // 31
   };

   public static void main(String[] _) {

      final Vector<Entity> entities = new Vector<Entity>();

      final Net address = new Net("address[3:0]");
      final Net enable_address = new Net("enable:address[6:4]");
      final Net data = new Net("data");

      final String address_item_type = IRON_ORE;

      final int X_END = 96;
      final int Y_END = 72;
      final int POLE_SPACING = 6;

      // requester chests and smart inserters
      //
      final Entity[][] map = new Entity[X_END + POLE_SPACING][Y_END + POLE_SPACING];
      for (int bx = 0; bx < X_END/3; bx++) {
         final int tx = bx * 3;
         for (int by = 0; by < Y_END/3; by++) {
            final int ty = by * 3;

            final int char_x = bx >> 1; // 0..15
            final int char_y = by >> 2; // 0..5
            final int char_code = 32 + char_y * 16 + char_x; // 32..127

            // bytes in char are organized as follows (0 being top, 7 being bottom):
            //    04
            //    15
            //    26
            //    37
            //
            final int bi = (bx & 1) * 4 + (by & 3);

            // bits within a (4 box / 4 inserter) cell are organized as follows:
            //    0:1 4:5
            //    2:3 6:7
            //
            for (int i = 0; i < 4; i++) {
               int dx = 0;
               int dy = 0;
               final int b = font.get_byte(char_code, bi);
               final int bits;
               switch (i) {
                  case 0:
                     bits = b;
                     break;
                  case 1: 
                     bits = b >> 2;
                     dy = 2;
                     break;
                  case 2:
                     bits = b >> 4;
                     dx = 2;
                     break;
                  default:
                     bits = b >> 6;
                     dx = dy = 2;
                     break;
               }
               final RequesterChest rc = new RequesterChest();
               rc.set_net(GREEN, data);
               map[tx + dx][ty + dy] = rc;
               rc.add_request_filter(item_encoding[bi * 4 + i], encode_bits(bits & 3));
            }

            for (int i = 0; i < 4; i++) {
               final SmartInserter si = new SmartInserter();
               int dx = 1;
               int dy = 1;
               switch (i) {
                  case 0:
                     si.dir = RIGHT;
                     dy = 0;
                     break;
                  case 1:
                     si.dir = DOWN;
                     dx = 2;
                     break;
                  case 2:
                     si.dir = LEFT;
                     dy = 2;
                     break;
                  default:
                     si.dir = UP;
                     dx = 0;
                     break;
               }
               map[tx + dx][ty + dy] = si;
               si.set_net(RED, address);
               si.set_net(GREEN, enable_address);
               si.add_condition(RED, address_item_type, EQUALS, char_code & 0xf);
               si.add_condition(GREEN, address_item_type, EQUALS, 8 + (char_code >> 4));
            }
         }
      }

      // medium electric poles
      //
      boolean xeven = false;
      for (int tx = 1; tx < X_END + POLE_SPACING; tx += POLE_SPACING) {
         xeven = !xeven;
         boolean yeven = false;
         for (int ty = 1; ty < Y_END + POLE_SPACING; ty += POLE_SPACING) {
            yeven = !yeven;

            // place medium pole
            //
            final MediumElectricPole p = new MediumElectricPole();
            map[tx][ty] = p;

            // wire up enable:address networks to smart inserters around pole
            //
            for (int dx = 1 - POLE_SPACING; dx <= POLE_SPACING; dx++) {
               final int x = tx + dx;
               if (x >= 0 && x < X_END) {
                  for (int dy = 1 - POLE_SPACING; dy <= POLE_SPACING; dy++) {
                     final int y = ty + dy;
                     if (y >= 0 && y < Y_END) {
                        final Entity e = map[x][y];
                        if (xeven == yeven) {

                           // wire up enable:address networks to smart inserters around pole
                           //
                           if (e instanceof SmartInserter) {
                              connect(RED, p, e);
                              connect(GREEN, p, e);
                           }
                        } else {

                           // wire up data network to requester chests
                           //
                           if (e instanceof RequesterChest) {
                              connect(GREEN, p, e);
                           }
                        }
                     }
                  }
               }
            }

            // wire up to adjacent poles in same networks
            //
            if (tx - POLE_SPACING >= 0 && ty - POLE_SPACING >= 0) {
               final Entity p2 = map[tx - POLE_SPACING][ty - POLE_SPACING];
               if (xeven == yeven) connect(RED, p, p2);
               connect(GREEN, p, p2);
            }
         }
      }

      // build up entities list
      //
      for (int tx = 0; tx < X_END + POLE_SPACING; tx++) {
         for (int ty = 0; ty < Y_END + POLE_SPACING; ty++) {
            final Entity e = map[tx][ty];
            if (e != null) {
               e.set_tile_pos(tx, ty);
               entities.add(e);
            }
         }
      }

      check(entities);
      out(entities);
   }
The "font" class (not included above) provides the "get_byte" method, which provides one of 8 different bytes for 96 different characters. (I had fun last night quickly whipping up my own bitmap font.)

Note that the above code is using a new feature I am still working on (the "Net" class). Nets are a higher level concept than wires - they represent an entire connected network of wires. The idea is that when I declare a bunch of entities to be connected to a given net, the constraint checker will eventually (it doesn't do it yet) verify that:
1. all items in that net are in fact wired together (my current character ROM would actually fail this check as it currently requires a very small amount of extra wiring to connect things together, but I've just realized how to fix that without adding any new poles), and
2. no two networks are accidentally wired together.
I also want to (optionally) use the net concept for generating simpler XFig diagrams -- all of the poles and wires are part of what makes Factorio logic hard to read, so just showing the net (and then having the constraint checker verify that the net is a 100% reliable representation of what you actually get in the blueprint) is a win in terms of diagram readability. (I also have some ideas for making the configuration of requester chests and smart inserters much easier to read, some of which I'm already using in my hand-made XFig diagrams but I haven't started putting into my automated "export" function.)

Minor warning: The blueprint string generated by the above is 1,349,163 bytes. It takes about a couple seconds just for the text-box paste operation to finish. (Blueprint string mod normally uses a gzip/base64 encoding, but also accepts strings that are just "plain text". Currently I'm just doing plain text because that's easier for me when it comes to debugging my tool, but I believe I can easily add the gzip/base64 stuff at some point so the strings will be a bit smaller.)

Attached is a picture of me holding the blueprint over my base. (The character ROM is almost as big as my whole base, sans miners/smelters.)
zaubara wrote:Why not make an HDL compiler and generate the complete CPU?
The thought has most definitely crossed my mind. :-)

However, the idea has some serious problems, and that is that there are many clever things you can do in Factorio that an HDL compiler can only do if it knows about every such clever thing and if it's smart enough to recognize when they can (and should) be applied. For example, there are many different ways to do logic in Factorio, depending on whether you want speed (and how much) or density (and how much) or low resource usage, and depending on what you're trying to interface with (e.g., how data is encoded, what kind of timing and timing controls are used) and what kind of "assembly scheme" you want to use. Also, the synthesis problem in Factorio (if one were to try to make such a smart/flexible synthesizer) is arguably more difficult than the problem of making an HDL synthesizer for an FPGA, and those things costs millions of dollars worth of man-hours to develop. (And then they take hours to run! - as soon as the problem gets hard, those programs get really slow!)

For example, my character ROM is designed such that the interface to it is just 3 wires - 2 in, 1 out -- that's it. It lets you read any of the 96 characters and gives you all 8 bytes of that character at once on just the one output wire. (That's why it uses 32 materials - it encodes 2 bits with each material.) The plan is that the logic chunk and the mechanical chunk for my "hi-res" display will also use very few wires for connecting with each other (and with the character ROM). So my "assembly scheme" is basically KISS -- keep the complex stuff inside the individual (generated, so much more trustworthy and easier to fix) blueprints, making wiring them together in the game so trivial even I will have a hard time screwing it up. Also, if you try to understand my character ROM you might run into a little confusing issue that it is not using a static logic scheme, but dynamic logic. (Using dynamic logic basically doubles the storage capacity of the ROM while also making it twice as fast -- win/win.)

That's not to say I won't add some automation/smarts to my blueprint generator (like maybe an auto-poler/wirer, or auto-placement, or specifying some HDL-style math/logic for how smart inserters and requester chests are configured so that abstraction can be used to make XFig diagrams much less busy), but I'm going to be selective about it and not force any given solution. My goal is to automate the boring/repetitive/error-prone bits and make readable diagrams, not suck the fun (or the Factorio-ness) out of designing stuff.

(Plus, if you want such an HDL system you could completely write one on top of what I'm creating -- because I am taking a "let the user do whatever they want" approach, there shouldn't be any artificial restrictions preventing such a use of it.)
zaubara wrote:So basically a printer which spits out 2 different items according to the ROM data? Nice!
Actually I'm thinking 4 or more different items (for more colors). The ROM data only contains on/off data, but that doesn't mean I can't provide foreground/background color data when I tell the whole machine to spit out the next character. (Strikeout, underline, italics and bold are also options I might include, but I haven't thought them through yet to see how hard they would be.) Also, my current plan is to treat my font as proportional (leaving out redundant blank columns) to make maximum use of my belt space. (In fact if that ends up not working out I'll have to fix my font to not all be left-justified as it is now.)
zaubara wrote:8 bits in 4 chests, ok, but 4 inserters? How?
Both code and hints are above. Another hint: My dynamic logic is "negative" logic, not "positive" logic. I.e., it's not about what's in the boxes that are wired up, it's about what's not in the boxes (because it's currently in-flight in the hands of inserters). My whole CPU design is pretty much based on dynamic-negative logic because it's very fast -- results pop out in game-tick time, not in box-to-box inserter-movement time.

But now that you've pointed out that different inserters and materials can share boxes, I'm wondering if I could make mine even smaller... I'll have to give it some thought.
zaubara wrote:NOP, ADD, ADDI, ADDC, SUB, SUBI, SUBC, AND, ANDI, OR, ORI, XOR, XORI, SHL, SHLC, ROL, SHR, SHRC, ROR, ASR, CMP, CMPI, CPC, JMP, JMPR, JZ, JZR, JNZ, JNZR, JCS, JCSR, JLS, JLSR, JGT, JGTR, CALL, RET, LDI, MOV, IN, INR, OUT, OUTR, OUTI
What's the difference between SHLC and ROL? (If the difference is that SHLC makes the Z flag work across multiple nibbles and ROL does not, I think that ROL could just be dropped as it's not going to be a very commonly used instruction.)
Where's AD[D]CI, CPCI and SBCI? In fact, the 'C' concept could be extend for logic ops as well: ANDC, ANDCI, etc. That way every operation you support can be synthesized for larger data sizes and it "just works". Though at that point 'C' should definitely be thought of as "continuation" rather than "carry".
zaubara wrote:But I am not quite sure what you mean by keeping the Z flag over a synthesized operation (ex 8 bit addition), to be more precise, what impact on the design it has, I can see the need for this...

If you have AVR Studio, start it up, go to the Help menu and select "AVR Tools Users Guide", then under "AVR Assembler/Instructions" select "CPC". There you can find how the AVR handles the Z flag:

Code: Select all

Z: R7 * R6 * R5 * R4 * R3 * R2 * R1 * R0 * Z    Previous value remains unchanged when the result is zero; cleared otherwise.
Compare that to the same text for the "CP" instruction:

Code: Select all

Z: R7 * R6 * R5 * R4 * R3 * R2 * R1 * R0    Set if the result is $00; cleared otherwise.
So the non-'C' version always sets/clears the Z flag only according to the current result, while the 'C' version never ever sets the Z flag! The 'C' version can only clear the Z flag, which it does if it ever sees a non-zero value in the current result.

So say I have one 32 bit number in registers r0:r1:r2:r3 (least significant bits in r0) and another 32 bit number in r4:r5:r6:r7. I can then do:

Code: Select all

   CP  r0, r4 ; Z set iff: r0 == r4
   CPC r1, r5 ; Z set iff: r0 == r4 && r1 === r5
   CPC r2, r6 ; Z set iff: r0 == r4 && r1 === r5 && r2 == r6
   CPC r3, r7 ; Z set iff: r0 == r4 && r1 === r5 && r2 == r6 && r3 == r7
   BREQ someplace ; branches iff: r0 == r4 && r1 === r5 && r2 == r6 && r3 == r7
That then compares those two 32 bit numbers, and the BRanch-if-EQual instruction branches if-and-only-if all 32 bits of r0:r1:r2:r3 were equal to all 32 bits of r4:r5:r6:r7 because after the last "CPC" above the Z flag represents whether not the whole 32 bit result was zero, not just whether or not the last 8 bits of it was zero.

AVR also gets this right for SBC and SBCI - the Z flag represents the result of the entire multi-instruction subtraction, not just the last 8 bits. (Sadly, the AVR instruction architecture does not get this right for add or rotate operations, nor does it have such a concept of "extended operand size support" for bitwise instructions. An argument can be made that instruction space was tight enough and the use of the Z flag after such a rotate or bitwise multi-operation rare enough that it just wasn't worth using up the instruction space for both non-continuation and continuation versions of those. But there's really no excuse for the AVR implementing ADD/ADC in a fashion that is simply wrong since ADD and ADC are already consuming the required instruction space.)
zaubara wrote:Somehow i have to reset the Z flag prior to such an operation (or vice versa keep it during it), is it a common way to have the Carry-less instructions (ADD, SUB, ...) also resetting the Z flag (to not use an extra operation dedicated to reset the flag)?
I can't say that it's "common" in the case of the Z flag because so few architectures even bother to handle the Z flag correctly. It's of course extremely common in the case of the carry flag (I'm not even aware of a counter-example in any real hardware architecture), and it makes a lot of sense for an architecture to handle Z the same (what kind of instruction sequence is needed) way as it handles carry.

I would say if you only wanted a "reset C/set Z" instruction(s) and then only the 'continuation' type instructions (not even have the non-cotinuation flavors) that would be fine, if a bit verbose, and perhaps unprecedented, but at least not logically incoherent. However, if you are going to have both 'non-continuation' and 'continuation' versions of instructions (like SUB and SUBC), then it just doesn't make sense for SUB to not set/reset Z without regard to previous operations. In other words, if you're going to have both SUB and SUBC, then they should work just like how SUB/SBC (and CP/CPC as shown above) works on the AVR. (But also make ADD/ADDC work like that too - no need to imitate that particular AVR ugliness.)
Attachments
char_rom_blueprint.jpg
char_rom_blueprint.jpg (555.15 KiB) Viewed 6001 times

Patric20878
Fast Inserter
Fast Inserter
Posts: 160
Joined: Tue Feb 24, 2015 4:53 pm
Contact:

Re: Binary Logic (UPDATE: 4-Bit CPU simulation)

Post by Patric20878 »

@zaubara: Nice. I was sure you must've at least been there before.

Never did get into the computing aspect of redstone circuitry much myself though. Focused more on compacting redstone designs instead, like my 12 hour digital clock (the last three pics in my TC imgur album, in vanilla redstone) and a 4x2x3 2 way repeater with 2 ticks delay, iirc. Contributed some to the RDF, though like you, wasn't a regular there. Sure would like to learn though. Stopped shortly after building my first 8-bit ripple-carry adder-subtractor and part of a multiplier, where TC took my attention instead.
Tekkit Classic expert and admin of the Tekkit Classic Wikia specializing in factory and frame gunship engineering, creator of the Optimized Steam Engine Setup, and a huge fan of Touhou. My TC designs may be found at https://imgur.com/a/IT0Ya.

NotABiter
Fast Inserter
Fast Inserter
Posts: 124
Joined: Fri Nov 14, 2014 9:05 am
Contact:

Re: Binary Logic (UPDATE: 4-Bit CPU simulation)

Post by NotABiter »

All of the below code (which by the way, turned out to be buggy - the constraint checker caught it once I implemented the nets vs. wires consistency checks):

Code: Select all

   // wire up enable:address networks to smart inserters around pole
   //
   for (int dx = 1 - POLE_SPACING; dx <= POLE_SPACING; dx++) {
      final int x = tx + dx;
      if (x >= 0 && x < X_END) {
         for (int dy = 1 - POLE_SPACING; dy <= POLE_SPACING; dy++) {
            final int y = ty + dy;
            if (y >= 0 && y < Y_END) {
               final Entity e = map[x][y];
               if (xeven == yeven) {

                  // wire up enable:address networks to smart inserters around pole
                  //
                  if (e instanceof SmartInserter) {
                     connect(RED, p, e);
                     connect(GREEN, p, e);
                  }
               } else {

                  // wire up data network to requester chests
                  //
                  if (e instanceof RequesterChest) {
                     connect(GREEN, p, e);
                  }
               }
            }
         }
      }
   }
Has now been replaced by this (which was missing before - I just didn't realize it because nets weren't really doing anything before):

Code: Select all

   if (xeven == yeven) {
      p.set_net(RED, address);
      p.set_net(GREEN, enable_address);
   } else {
      p.set_net(GREEN, data);
   }
And this one little magical line:

Code: Select all

   auto_wire_nonpoles(entities, errors);
The auto-wirer takes into consideration red/green wire coloring, net assignments, and the distance any given pole's wires can reach to determine suitable candidate poles, and then wires to the closest one. It doesn't do pole-to-pole wiring - it just takes everything else (that's assigned to a net) and wires it up to poles.

And these two lines get augmented with an 'errors' parameter:

Code: Select all

   check(entities, errors);
   export(entities, errors);
which lets the auto-wirer and the constraint checker visually indicate where errors occur.

Last post I mentioned my nets weren't fully connected. I've fixed that of course, but I left it broken for the attached image to show how the constraint checker (in addition to generating the error message "multiple disconnected subnets exist for net 'address[3:0]'") is indicating where it found the problem -- it's highlighting one of the disconnected subnets with the purple X's and another one with the red X's. (Similarly, if the auto-wirer complains because there's no appropriate pole to wire an item to, it will throw an 'X' on the item.)

Finally, the little red/green/blue boxes are the net indicators, with the colors being set via a new Net constructor parameter:

Code: Select all

      final Net address = new Net("address[3:0]", Fig.BLUE[3]);
      final Net enable_address = new Net("enable:address[6:4]", Fig.RED[3]);
      final Net data = new Net("data", Fig.GREEN[3]);
A net indicator in the upper-left corner of a tile means the item in that tile connects to that net via red wire. A net indicator in the lower-right corner of a tile means the item in that tile connects to that net via green wire. In an apparent attempt to confuse people, I have my "red net" running over green wire. :-)
Attachments
disconnected_subnets.png
disconnected_subnets.png (708.92 KiB) Viewed 5969 times

Post Reply

Return to “Mechanical Throughput Magic (circuit-free)”