The Cylindrical Haptic Asynchronous Robot Logic Interface (CHARLI) is a robot that can be made by hand out of easily available parts. It was originally designed to test the algorithms for tiny microrobots because it is much easier to play with CHARLI, which fits nicely in the palm of your hand, than with something the size of a grain of sand. VITOBS allows CHARLI to be designed with Verilog similar to what would be used in an actual microrobot; however, this page focuses on CHARLI as a toy to illustrate VITOBS.


CHARLI's name is an acronym. The "H" in CHARLI stands for the word haptic, which means touch. In robotics, haptic normally describes a sensitive kind of touch emulating what people feel. Much cruder than that, CHARLI navigates only by a sense of electrical "touch". CHARLI's sense of touch is binary—either it is touching or not touching. CHARLI knows when it has come in contact with another robot or with the edge of its world when it makes electrical contact. CHARLI has no optical sensors that would allow it to plan its course or avoid obstacles. The "C" in CHARLI stands for cylindrical. CHARLI needs to be this shape because the cylinder has no sharp corners that could interfere with other robots. Since fabricating a precise cylinder by hand is difficult, CHARLI's chassis, shown in Figure 2, is made with a tuna-can lid. (see .pdf with detailed instructions)





Figure 2. CHARLI before Basic Stamp and battery are installed.



The "LI" in CHARLI stands for logic interface. CHARLI uses an open-collector logic interface, which operates in two modes: a sensing mode and sending-0 mode. When CHARLI is in the sensing mode but not touching anything, it reads a 1. When a first robot is in the sensing mode and touching another robot (or the edge of its world) that is in the sending-0 mode, the first robot reads the 0 given to it by the second robot. In electrical terms, the sending-0 mode is simply being connected to ground; the sensing mode is disconnecting the output and floating to +5 volts (through one of the pull-up resistors shown in Figure 1). The robots operate on a metal surface to give them a common reference to ground. There are no other connections between the two robots. The "A" in CHARLI stands for asynchronous, which means the clock on one robot runs at a slightly different rate than on another robot. This is unavoidable (since they cannot share a common clock), but it actually is a feature. If they could be synchronous, two robots could operate in lock-step, each being in sensing mode and sending-0 mode at the same time, which would mean the two robots would be touching, but neither would realize it. With proper state-machine design (the thing that the VITO tools are all about), the asynchronous nature of the interaction of two robots will allow one or the other eventually to realize it is touching something.



Figure 1. CHARLI Schematic.


CHARLI uses its tuna-can lid (sorry for the pun, Charlie) to mount two inexpensive DC motors (at a 45 degree angle to the surface) with grommets as wheels, as shown in Figure 2. On top of this, an embedded microprocessor, BASIC Stamp 2, controls the motors using FAN 8082 integrated-circuit H-bridges. The can rim provides a docking ring. Appropriate lengths of aluminum foil are attached to the rim with double-sided tape and connected to pull-up resistors shown in Figure 1 for open-collector operation. Multiple CHARLI robots may interact on a metal surface. Each robot has an aluminum-foil brushing to provide a common ground for reliable open-collector operation. CHARLI is powered by an onboard 9-volt Ni-MH rechargable 30g battery. (An alkaline battery is too massive for CHARLI to carry easily.) These parts are all readily available, and, excluding the microprocessor, cost less than ten dollars. Of course, any inexpensive embedded microcontroller could be used; CHARLI uses the somewhat more expensive BasicStamp2 because, in a single chip, it includes: the microprocessor, RAM, flash, an oscillator, a voltage regulator and a serial debugging interface (here via stereo jacks in Figure 1, which weigh less than traditional DB9).


CHARLI's behavior is described by a state machine written in implicit-style Verilog. From this, VITOBS generates an embedded-software emulation on the BASIC Stamp, making CHARLI's logical behavior nearly identical to that of a microrobot controlled by such Verilog. The BASIC code output by VITOBS (in the .bs2 file) is merged with other BASIC “wrapper” code (in .bs0 and .bs1 files) that emulates the operating environment of the microrobot and interfaces to the docking ring. (A DC-motor does not normally act like a microrobot, and it takes some BASIC code hidden in the wrapper to emulate it, particularly pulsing the motor for a shorter time than the clock period.) Because embedded software is much slower than digital logic, the major cycle for CHARLI is on the order of a second. For testing purposes, this is actually useful as it allows careful observation of CHARLI's action. Because the complexity of a state machine effects the execution speed of the embedded software, there will be a wider variation between different robots' “clock” speed than if the state machines had been implemented in hardware. The BASIC code uses the RS-232 serial port provided by the Basic Stamp 2 to output debugging information, such as the current state. (VITOBS automatically generates such a BASIC subroutine for debugging; the wrapper code is free to perform a "gosub" to this routine.)


In Figure 1, outputs bits 0-3 connect to the two H-bridges and thereby to the two motors. (These four bits are referred to as the BASIC built-in variable out0, out1, out2 and out3.) Making all four of them zero stops both motors. Briefly pulsing one of these causes one of the motors to move slightly. Because the duration of the required pulse is shorter than the VITOBS clock, the wrapper code (in .bs0) needs to reset these commands back to zero well before the full clock cycle is over. Since these built-in variables do not remind us of what motion is involved, we will define Verilog macros that assign a 1 to the appropriate variable: `FORWARD_LEFT (which we define as the left, i.e. north-side, motor jumping slightly east), `REVERSE_LEFT (left motor jumping slightly west), `FORWARD_RIGHT and `REVERSE_RIGHT. Alternately doing `FORWARD_LEFT and `FORWARD_RIGHT causes CHARLI generally to move towards the east; alternately doing `REVERSE_LEFT and `REVERSE_RIGHT causes CHARLI to go west. (Other possible motions not included in this example include alternately doing `FORWARD_LEFT and `REVERSE_RIGHT, which causes CHARLI to rotate clockwise in place; and repeatedly doing `FORWARD_LEFT, causes CHARLI to make a wide clockwise turn.)


Input bits 12-15 (individually available with the built-in BASIC Stamp variables in12, in13, in14 and in15, or collectively as ind) provide the connections to the docking ring on the east, north, west and south sides, respectively, as shown in Figure 1. When CHARLI is not touching anything, ind will read as 4'b1111. If CHARLI's east side touches something, ind will read as 4'b1110. It is more convenient to deal with the complement of this; the Verilog code below uses ~ind, as discussed below.


The BASIC Stamp allows the same pin to act as input or as output at different times. This is called changing the direction of the port (either forcing it to output a 0, or letting it sense whether there is any external influence to its pull-up resistor.) The built-in BASIC variables dir12, dir13, dir14 and dir15 (collectively know as ind) control port direction for the east, north, west and south sides, respectively. Again, the built-in variables are cryptic; we define macros like `SEND_0 and `SENSE_SWN as equivalent to assigning appropriate values into dird.


With this background, we can explain the charlibounce12.v code in the examples directory. First, there are the macros:

  `define FORWARD_LEFT out3 <= 1
 
  `define REVERSE_LEFT out2 <= 1
  `define FORWARD_RIGHT out0 <= 1 

  `define REVERSE_RIGHT out1 <= 1 

  `define SWNE 4'b1111
  `define SWN  4'b1110
  `define SNE  4'b1011
  `define SENSE_SWN dird <= 4'b0001
  `define SENSE_SNE dird <= 4'b0100
  `define SEND_0 dird <= `SWNE 

Remember, the <= is the non-blocking assignment. Each of these involves a built-in BASIC Stamp variable that impacts the pins on the chip during the next clock cycle. Following this are the usual module declarations and ports, which is ignored by VITOBS:


  module charli(sysclk,reset,ind,out0,out1,out2,out3,dird,ind);

  input sysclk,reset;
  input [3:0] ind;
  output out0;
  output out1;
  output out2;
  output out3;
  output dird;
  wire [3:0] ind;
  reg out0;
  reg out1;
  reg out2;
  reg out3;
  reg [3:0] dird;

VITOBS ignores the reg declarations for the built-in BASIC out and dir variables (because they cannot be declared in user code), but does take notice the reg declarations for user-defined variables:


  reg stepleft;
  reg [3:0] touch;

and generates the appropriate BASIC declarations. As with all implicit Verilog state machines, the code that VITOBS translates to a one-hot controller occurs in an always block. The following shows the first 21 lines of this always block:


  always
   begin
     `SEND_0;
    @(posedge sysclk);
     while (!touch) //(touch == 0)
      begin
        if (stepleft)
          `FORWARD_LEFT;
        else
          `FORWARD_RIGHT;
       @(posedge sysclk);
       @(posedge sysclk);
        stepleft <= ~stepleft;
        `SENSE_SNE;
       @(posedge sysclk);
        touch <= (`SWNE^dird)&(~ind);
        `SEND_0;
       @(posedge sysclk); 
     end
     stepleft <= ~stepleft;
     touch <= 0;

The first thing that happens is to put all four sides of the robot into the sending-0 mode (the macro `SEND_0 is equivalent to dird <= `SWNE, which in turn is equivalent to dird <= 4'b1111). This code depends on the fact the BASIC Stamp initializes all user-defined variables to zero, which means the while loop involving (!touch) will be entered. (If this code were synthesized by VITO into hardware, a small amount of additional code would be required to make this happen, which we omit here to keep this example as simple as possible.). (!touch) is equivalent to (touch==0), but the former generates more efficient BASIC code. Each time through this while loop, the stepleft variable will alternate between 0 and 1, causing the motor to alternate between `FORWARD_LEFT and `FORWARD_RIGHT. After the robot has moved, there is an empty clock cycle allowing another robot the chance to sense this robot's sending-0 mode. After this, the robot changes to its sensing mode on the south, north and east sides. (Because it is going forward (eastward), these are the only sides where its own motion, by itself, could touch something. If another robot touches its west side, it would be due to that robot's motion, and this robot's westward send-0 will tell the other robot to back off as this robot continues eastward.)


The assignment to touch is the most obscure aspect of this code. Because of `SENSE_SNE the current value of dird is 4'b0100, We need a mask which is the 4-bit complement of this, 4'b1011. The exclusive OR, (`SWNE^dird), which is equivalent to (4'b1111^dird) gives us this mask, which is then used to AND the complement of ind. The reason for this mask is to avoid seeing the input from the west side, which this robot is currently ignoring (as explained above). This code is logically correct, and would work if synthesized into hardware, but one might ask if there were simpler ways to accomplish the same thing, like 4'b1011&(~ind)? The answer is yes for hardware, but no for the BASIC Stamp, for reasons explained at the end of this page. The while loop exits only if the robot has sensed something on the souch, north or east sides. The variable stepleft (which was complemented inside the loop) is complemented again after the loop to restore its value to what it was when the robot first touched something.


The remaining part of the always block is a nearly identical while loop, with two distinctions. First, the motor will alternate between `REVERSE_LEFT and `REVERSE_RIGHT. (Because stepleft was restored to the value that caused the robot to touch something, the side of the robot that last moved forward will be the the side that begins to back away. This helps reduce the chance the robot will get stuck.) Second, since the robot now moves westward, it uses sensing mode on the south, west and north sides. (These are now the only sides where its own motion, by itself, could touch something. If another robot touches its east side, it would be due to that robot's motion, and this robot's eastward send-0 will tell the other robot to back off as this robot continues westward.)


    @(posedge sysclk);
     while (!touch) //(touch == 0)
      begin
        if (stepleft)
          `REVERSE_LEFT;
        else
          `REVERSE_RIGHT;
       @(posedge sysclk);
       @(posedge sysclk);
        stepleft <= ~stepleft;
        `SENSE_SWN;
       @(posedge sysclk);
        touch <= (`SWNE^dird)&(~ind);
        `SEND_0;
       @(posedge sysclk); 
      end
     stepleft <= ~stepleft;
     touch <= 0;
    end
  endmodule

As happened when exiting the previous while loop, on exit from this loop the variable stepleft (which was complemented inside this loop) is complemented again to restore its value to what it was when the robot first touched something. When the always takes the code back to the top and the previous while loop occurs again, this restoration of stepleft will cause the side of the robot that last moved in reverse to be the the side that begins to move forward again.


Here is a video showing the interaction of two robots configured with the above Verilog.



The assignment to touch involves dependencies on ind and dird. Even if this had been coded as touch <= 4'b1011&(~ind) in the first loop or touch <= 4'b1110&(~ind) in the second loop, the architecture of the BASIC Stamp demands that there be a hidden dependency on dird (because ind cannot be input into the chip unless dird has been set to allow the three relevant bits to act as inputs). By coding this as touch <= (4'b1111^dird)&(~ind), VITOBS is able to recognize this dependency and generate the correct code. This expression also has the advantage of working identically in both while loops.