FPGA Radar
FPGA Radar
Brian Plancher
December 8, 2015
Contents
1 Introduction 2
4 Testing 21
A Bibliography 25
B Verilog Code 26
1
Chapter 1
Introduction
Imagine that NASA’s Curiosity rover suffered a catastrophic failure to its guidance and
communications systems. All that remains of the system is the backup IR communication
receiver. The only system close enough to communicate with Curiosity is the now stationary
Spirit rover which got stuck in some soft soil in 2009 (suspend disbelief that both the rover
came back on-line) which could then communicate back to earth. How could NASA control
the robot’s movements and keep the mission going?
This project is an exploration of a solution to that problem. Namely, I want to have the
stationary device locate the position of the “rover” through ultrasound location sensing and
be able to send it instructions to find its orientation and as a stretch send it to a desired
location by calculating a path between the “rover” and a destination.
2
Chapter 2
The project can be understood as two interacting systems as shown in Figure 2.1. The
primary system is the main FPGA system which consists of 4 main blocks: the ultrasound
distance block, the orientation and path calculation block, the IR transmitter block, and the
VGA display block. The secondary system is the “rover” system which consists of 2 main
blocks: the IR receiver block, and the motor control block. The blocks at a high level all
operate as their name implies and only do that one task. However, I found that at the end
of the day I needed to insert a main FSM into the Orientation and Path Calculation Block
which also told the Ultrasound Block when to proceed.
3
2.2 Design Decisions and Motivation
The primary motivation for this project is to explore robotics, signal processing and control
systems and gain experience building systems. I am incredibly interested in the topics and
am trying to pursue further graduate study in those topics. I therefore see this system
as a starting point and testbed for further work. While all of the modules are necessary
to complete the project, multiple modules can have their outputs hard-coded to allow for
testable intermediate steps (e.g., always define the path as a straight line forward). Because
I am starting from a point of limited experience with many of the technologies I am working
with I made a few design decisions to increase the likelihood of completion. For example, I
decided to use IR and sound communication instead of RF communication because I worked
with them in earlier Labs and given the complexity of the system did not want to add
another new technology. It also reduces the need for obtaining extra hardware. I am also
going to work with a two-motor tank platform for the “Rover” base since I already own that
platform. I also already own some Ultrasound sensors that I have been wanting to use that
I am excited to take advantage of in this lab.
At the highest level, the project in its completed form is designed to allow the Main FPGA
to leverage the ultrasound sensors to calculate the “Rover’s” location (potentially vis-a-vis a
target location) and send it a command over IR to discover its orientation (and potentially
move to the desired location). The system will output VGA to show the location. Timing
delays will only become important if I end up implementing the feedback control but until
then most things are stable and inputs can be pipelined through as many registers as need
to get the calculations done. To begin with I am going to assume away the orientation
calculation and just solve the Ultrasound Block and VGA Block as all other steps are built
on top of that step.
As an important note, I had originally proposed using multiple microphones and a sound
source on the “Rover” to triangulate the position of the “Rover” but found out from Gim
that the Nexys4 board had some limitations that would have made that implementation
very difficult. Therefore I have adjusted to the Ultrasound sensor based approach.
4
2.3 Successes and Challenges
Specific successes and challenges for each module will be explained later in the paper, but
there were a few key overarching successes and challenges that bear mention.
First, I had originally proposed using multiple microphones and a sound source on the
“Rover” to triangulate the position of the “Rover” but found out from Gim that the Nexys4
board had some limitations that would have made that implementation very difficult. There-
fore I have adjusted to the Ultrasound sensor based approach.
Secondly, the biggest challenge I faced was interfacing with the many different hardware
modules I used in my project. Each one had different quirks and created a lot more work
beyond getting the digital logic correct.
Finally, given the Ultrasound approach I think I was quite successfull in the context of
the proposed project. My setup, while only accurate to 30 degrees, does correctly find the
“Rover” and does determine its orientation correctly. It also even correctly calculates the
path it needs to move to reach the target. However, given accuracy limitations in the angle
measurements and in the consistency of traction from the “Rover” the path execution leaves
a bit to be desired. If I had more time, I would have loved to use a stepper motor to take
many more readings with the ultrasound to provide a much more accurate idea of where the
“Rover” was located and to provide a feedback based control to “Rover” to make sure it
reached the target even if it took more than one move. However, since the move was entirely
a stretch goal to begin with, I am quite happy with the overall project.
5
Chapter 3
This section goes into more detail into each block in the Main FPGA which consists of the
majority of the Verilog code as it not only locates the rover and determines its orientation
and path to travel, but also displays the VGA output. The more detailed block diagram
of the Main FPGA can be found below in Figure 3.1 and futher sections may provide even
more detailed block diagrams.
Figure 3.1: A detailed block diagram showing high level links between the various modules
6
3.1.1 Ultrasound Block
The Ultrasound Block is probably the most important block in the entire system as its
accuracy determines the precision and ability of the rest of the project. The detailed block
diagram can be seen below in Figure 3.2.
The design was based around the specifications of the HC-SR04 ultrasound module which
I used for the range finding. Given this and the fact that the HC-SR04 ultrasonic module
operates on 5V I dedcided to use the Labkit for my main FPGA to provide easier integration
with its 5V power supply (this is also helpful again for the IR transmission). The way the
module works is when it is triggered it sends out the pulse and determines the location of the
nearest object in its range and then sends the echo output too high for a time proportional
to the distance to the nearest location as show in Figure 3.3 below.
Figure 3.3: Timing diagram of the ultrasound module from ELEC Freaks
7
The module has a field of vision of about 30 degrees from practical testing done by Cytron
Technologies as show in Figure 3.4 below. This meant that I had to either rotate one module
with a stepper motor or use multiple modules to cover a full arc from the main FPGA. Given
time constraints I started with using 6 modules attached to a block of wood cut to angles
allowing for full 180 degree viewing with the center of each module located at 15 + 30i
degrees for ultrasound modules 0 to 5. Therefore, to calculate the position of the object
with accuracy I leveraged some simple geometry and triganomietry and too the shortest
distance received as the distance to the “Rover” r and use that angle θ to calculate its
position in (x, y) space. Using the fact that x = rcosθ and y = rsinθ. Since I only used 6
pre-defined angles from 6 modules the cos and sin of those angles can be precomputed and
stored in memory or registers for quicker executing during operation (see Orientation and
Path Calculation block for more information on the calculation).
Figure 3.4: Practical range of the ultrasound module from Cytron Technologies
While this all seemed theoretically straight forward, I ran into a large amount of issues when
attempting to execute the system. The largest problem I had to tackle was the fact that my
modules tended to get stuck in a constant high output state after a few uses. It seemed that
the modules could only be trusted for a few uses after turned on. This was highly concerning.
After exploring the issues further and testing when it occurred, I was able to conclude that
it happened whenever the sensor did not locate a target. This was confusing because the
timing diagram (see Figure 3.2) explicitly said it would send an extra-long signal if it saw
nothing and then reset automatically.
However, after internet research I determined that some of the modules come defective and
do not automatically reset. The only way to reset it (as I determined experimentally) was
to power cycle the module. Therefore, I worked with Gim to design a simple circuit to allow
8
me to control the power to the module with an additional signal. This required the use of
a PNP and an NPN transistor in order to switch on a 5 volt power supply with a 3.3 volt
signal from the labkit (see Figure 3.5 and 3.6).
These are now fully operationally and working. The error is completely gone. One note
though is that it greatly slows down the time it takes to determine the position, which while
it doesn’t effect the outcome is not what I was looking for. For future implementers if you
purchase your modules on a steep discount be aware that they may not implement their spec
completely. Be prepared to not assume anything and try all corner cases when they don’t
work.
9
While the power cycle works perfectly this actually spawned another complication; the first
distance measurement after the power cycle was not always accurate and often retruned a
value of 0. Therefore, I did two things to weed out this issue. For one, I decided that a value
of 0 was always an error and threw out the value and would run the module again. Secondly,
I decided to always uese the median of 3 valid distance samples to weed out other errors
and increase overall accuracy. Implementing these further steps in the process removed this
error. The one risk moving forward if I continue to work on the project and impliment more
step angles on the ultrasound with a stepper motor is that 3 may not be enough samples if
error rates increase and I may have to add in more logic to sample 5 or even 7 times. The
good news is that adding in additional simples requires very little change to the code due to
the high level of parameterization of my Verilog modules. I highly suggest a liberal use of
parameters for future implementers. The final set can be seen below in Figure 3.7.
10
3.1.2 VGA Display Block
The VGA Display Block is at its core the same as Lab 3 and utilized the provided code for
that lab. However, it required a lot of custom Verilog in order to display the features that I
wanted to display as seen in the various modules in the block diagram as seen in Figure 3.8.
The core of the VGA block is the VGA Writer module. This module controlls the overall
VGA flow and combinations of the various shapes on the screen. It also pipielines all of
the signals appropriately as each signal completed at a different rate. It takes in the target
location, the rover location and orientation, and the xvga signals and then routs them to
the various submodules for mathematical transforms and pixel creation. First it computes
a polar to cartesian transform on the rover and target location which clears in less than one
clock cycle. This is passed to the blob modules for the unoriented rover and the target which
in turn clear before the end of the first clock cycle. This information is also passed along
with the orientation to the oriented blob module. That module take 4 clock cycles to clear.
Similarly, the grid module which simply takes in the current x and y value and computes
the background polar grid pixel value takes 4 clock cycles to clear. The appropriate rover
pixel depending on if the orientation is known is then alpha blended with the target pixel
and then the output is shown on the screen on top of the grid pixel. Early on, I did not
11
pipeline the output and got VGA signals that looked like Figure 3.9 below. For furture
implimentors, this type of rainbow VGA is a clear sign of a timing issue. Also if you open
up the synthesis report, at the bottom will be all of the timing issues. I found this report
extremely valuable as it will not only tell you there is a timing issue but let you know which
combined calculations take too long.
Diving a little deeper into the Grid and Oriented Blob modules, the modules require 4 cycles
to clear as they do a substantial amount of math to compute whether a given pixel should
be displayed. I realized early on that since I am only going to be using lines along pre-
determined angles I could continuously compute the values with multiplies and shifts and
then use comparisons to find which line (if any) a given point was on. This math can be
found in the modules themselves and simialr logic is used in submodules leveraged by the
orientation and path math (see the Orientation and Path Calculation Block). I also found
that when doing this that integer math errors would accumulate through the logic and I had
to apply rounding factors of increasing size as the errors accumulated to make the points
show up. This occured both on radial lines and arcs of constant radius. You can see an
example of this below in Figure 3.10 showing this issue with the arcs of constant radius.
The VGA Display block also has a Hex Display module which was simply used for debug
purposes and will leverage the provided code for the Hex Display. One thing to note for
future implimentors is that the Hex module on the labkit has timing issues in it. While you
can ignore these complaints by the compiler as the code will work, be aware that the display
may not be accurate for fast changing values.
Finally, due to timing constraints I was unable to get to the stretch goal of an automatically
scaling display or a more elegant image for the orientaiton beyond drawing a line in the
direction of orientation on the square representing the rover (see Figure 3.11).
12
Figure 3.10: VGA Rounding Issues
13
3.1.3 Orientation and Path Calculation Block
At the end of the day, the Orientation and Path Calculation Block first calculates the ori-
entation of the “Rover” by comparing the initial position to a position after a move straight
forward. It then cacluates the path to the target based on this orientation and new location.
When starting to integrate the various modules together I realized that despite my original
plan, in order to keep things simpler, I would need a Main FSM module to control the flow of
data. I decided to house it inside of the Orientation and Path Calculation Block as this block
does most of the heavy lifting and is quite integrated whereas the Ultrasound Block simply
waits to be told to calculate a location and returns that location and the VGA display block
simply displays the current state of knowledge constantly. I then decideded for simplicity
to split out the orientation math and the path math into two seperate modules. I would
later find that I needed to use the orientation math inside of the path math and was very
happy that I could just instantiate another copy of the module for a clean and guaranteed
correct calculation (based on my earlier testing of the orientation block). Further in order to
simplify testing and to pipeline from the start (based on my VGA experience which I started
earlier) I liberally used many sub-modules to calculate part of the math and allow for easier
testing. The detailed block diagram for this block can be seen below in Figure 3.12.
While the Main FSM controls the overall flow it is just a simple FSM with straightforward
state transitions. One thing I did learn during the process of making the FSM is that it
is often helpful to introduce one clock cycles delays between states in which you enable a
sub-module and then check if it is done. This is helpful because some of the sub-modules
14
won’t clear their done signal back to 0 until one clock cycle later and the FSM will cause a
state transition too early without the delay.
The Orientation Math module leverage a lot of the ideas behind the VGA Display Block
(Polar to Cartesian, using multiplies and shifts for given angle sin and cos calculations etc.),
but was simpler to reason about and debug because instead of being truely pipelined with
constant inputs it could pipeline through an FSM and delay the output as it only receives one
set of new data each couple of seconds. For simplicity of angle calculations across the VGA,
Orientation and Path math, I will only calculate the θ of the orientaiton to an accuracy of
15 degrees (with θ = 0 defined as facing the right). My math solves ∆x ∗ tanθ = ∆y (see
Figure 3.13) and finds the closest match within an error bound (again learning from the
VGA work to include an error bound). Once it is ready it will relay this information to the
VGA Display Block though the Main FSM.
While Path Calculation Mode was a stretch goal, I strove to complete it as part of my
interest in the whole project was robotic control. Whiel I ran into many complications with
the hardware in making it work well (see Motor Control Block later for more information on
these errors) I am excited to say that I completed the software side completely. Leveraging
my experience with the Orientation Math I realized that the angle the “Rover” would need
to turn is the different between its current orientation and the orientation needed to move
from its current location to the target location in a straight line. Therefore, I instantiated
another Orienation Math module inside of the Path Math module in order to compute the
orientation needed and then solve for the angle the rover needed to turn. After that I realized
p
that while the distance needed is r = (y2 − y1 )2 − (x2 − x1 )2 , I could also solve it without
needed a squareroot using triganometry. Given that needed orientation which I will call µ
15
(ytarget −yrover )
you can easily solve for the path length as path = sinµ
(see Figure 3.14).
With this in hand and some pipelined calculations through another simple FSM the path
math can easily be calcuated. What I did not have time to implement is a feedback based
control to adjust and correct the commands based on if the previous move was too far or
too short in both angle and distance traveled to get the “Rover” to the correct final location
in as few moves as possible. Ideally once this was implemented, since the “Rover” will be
moving of uniform level terrain it shouldn’t have to greatly adjust the scaling factor and
it should quickly come to an equilibrium solution and provide accurate commands moving
forward once it is calibrated (assuming all of the electronics perform to consistent levels). If
I had more time I would have loved to implement that and other even further stretch goals
could include the ability to add some sophisticated path finding algorithms with obstacles
to the calculation.
I’d also like to include in here that I also wrote a seperate simple top level module which
reads in the switches for the target location and translates it into a value in polar coordinates
like the rest of my values to make it easier for all of these modules to do math on the location
and display it.
16
3.1.4 IR Transmitting Block
The IR Transmitting Block sends the command from the Orientation and Path Calculation
Block. This command will be sent over my custom protocol to the “Rover” for execution.
The data will be sent as a 40kHz square wave with the start pulse lasting 2.4ms and the
logical “1” lasting 1.2ms and the logical “0” lasting 500µs like in Lab5b. Like in Lab 5b,
my 12 bit message is composed of a 5 and 7 bit piece of information. The 5 bit piece is the
angle of rotation and the 7 bit piece distance the “Rover” needs to travel in order to reach
the target. The information is sent LSB to MSB and the distance is always sent first (see
Figure 3.15 below for example of this type of protocol).
Figure 3.15: Timing for logical “1” and “0” over IR protocol and ordering of data from Lab5b
Since I used the labkit I had a nice constant 5V rail to use and in order to cover most of
the 180 degree field in which the rover could be located I used two IR transmitters each
attached to the same signal line. Figure 3.16 shows the final block diagram and wiring of
the transmitters below (per the Lab 5b spec).
17
3.2 Rover
The overall block diagram and final hardware setup for the rover blocks can be seen below
in Figure 3.17.
Figure 3.17: Block Diagram and Final Hardware Setup of the Rover
18
3.2.1 IR Receiver Block
The IR Receiver Block at its core operated identically to the block designed for Lab 5b as
it simply reads in the message from the IR signal and store it into a 5 bit angle register
and 7 bit distance register. Nothing really needed to change in the Verilog from Lab 5b as I
retained almost all of the protocol. However, on the hardware side I needed to use multiple
IR receivers to make sure that the rover could see the signal regardless of its orientation. I
ended up using 3 receivers and then using a 74LS10 and a 74LS04 to combine the signals
together to pass in a single value that the verilog could use as if it was simply a single input.
The circuit can be seen below in Figure 3.16 along with the wiring and the wiring of one of
the receivers (per the Lab 5b spec) that was located on the same board.
The one complication I reached is that the Nexys4 board only supplies a 3.3v output and
the IR Recievers need a 5v power supply. I therefore used a 4AA power pack with a voltage
divider (see Figure 3.18 for the circuit diagram) to reduce the voltage from 6v to 5v and
power the IR. I had to play with the resister values to find a dividing amount that provided
enough current to power 3 reciever circuits the 74LS10 and 74LS04 but not too much. I also
found that as the battery started to lose some charge I had to adjust the resisters to allow
for more current flow.
19
3.2.2 Motor Control Block
The Motor Control Block takes as input the decoded angle and distance from the IR Receiver
Block and then powers the motors on the tank base for the appropriate amount of time.
While I thought the motor control would be very simple and while it was simple on the
Verilog side (once I again added in a small main FSM), I ran into a bunch of hardware
complications. For one, I needed an H-Bridge circuit to drive the motors in both directions
to make turns and not simply go in a striaght line. It turned out that the small hobby
motors I was using had a very large current draw and needed more current than a simple
NPN/PNP transistor H-Birdge could provide. I was able to purchase online an L9110S H-
Bridge which when powered from a 6volt 4AA battery pack (seperate from the IR power
pack) could power the motors and make the motors move. However, I found that the only
way to accurately control the pulses was to hook the H-Bridge inputs to NPN transistors to
have them connect one input to ground and complete the circuit only when specified by the
Nexys4 inputs since they were seperately powered by the battery pack. These circuits can
be seen below in Figure 3.19.
Another bug I found with the motors was that I needed a seperate power supply for each
motor in order to have them be able to run in opposite directions simultaneously and have
the “Rover” turn effectively. When I tried to use the same power supply it created too strong
of a conflicting current drag on the battery and neither motor turned effectively. Now the
“Rover” turns on a dime and can effectively execute commands. The final bug I overcame
was that the rover moved a different amount per clock cycle on different surfaces. I found
that adding manual adjustor values through the switches on the Nexys4 board that can be
updated with the reset switch solves that problem perfectly.
20
Chapter 4
Testing
The basic testing for each module consisted of creating Verilog test benches that can be
simulated with ModelSim. In many cases I had to create both real hardware parameters
to use with the hardware modules in real life and test bench versions to allow modelsim to
complete in a reasonable amount of cycles and allow for intelligent debugging. Combinatorial
test benches were used for the interactions and combinations of the various modules that
interact. I also constantly used the hex displays and the logic analyzer to output data that I
was interested in testing in real life once things checked out in the modelsim runs. Finally I
did substantial user testing to make sure that all of the models worked correctly. Fortunately,
once I got the VGA display working I was able to use it to help me debug what the FPGA
though additional values of certain variables should be. A screenshot from ModelSim and
an image from user testing can be seen below in Figure 4.1 and 4.2.
If you pay close attention to Figure 4.2 below you will notice that I used tape to display
most of the VGA output on the ground so that I could directly and quickly compare the
location of the “Rover” in the real world to the location found on the display.
21
Figure 4.1: ModelSim run example showing multiple ultrasounds in use
22
Chapter 5
Looking back on the project I am very satisfied with the result and very glad that I chose this
project. I have leared so much both in regards to digital logic as applied to real hardware
systems, and in regards to interfacing, debugging, and building simple circuits for analog
hardware systems. I achieved my goal of gain some practical robotics knowledge and am
excited to continue working on similar projects in the future. One regret I have is that I
didn’t have any teammates because I could have reached more stretch goals and made the
project even better if I had a teammate and therefore more time to work on the project. On
the flip side, by working on it alone, I was forced to work with and gain a better fundamental
understanding of every aspect of the project, and I feel like a really understand now every
topic we covered in the course (with the exception of audio, but you can’t have one project
do absolutely everything!).
When evaluating my progress against the plan, I more or less stuck to the schedule that
I specified in my proposal. However, my time estimations were wrong for a number of
modules which was mainly driven by hardware compatability issues. It took a very long
time to determine that just how the HC-SR04 modules were deffective and how to solve that
problem and how to satisfy the current and voltage draw of the motors. I should have built
in much more time for hardware debugging and building. At the same time, the order in
which I built the modules was correct and provided me with many successful midpoints and
allowed for step by step debuggining of the integrated system.
I did plan my modules and the connections between modules in advance, but I did find
that I had to insert a Main FSM module in the middle of all of the logic to provide better
23
debugging and control over the connections and flow of signals especially given the many one
cycle delays I needed to insert into the logic. I should have done this from the beginning as it
realyl created no additional complications and simply made the full program run smoother
and made it easier to debug. I also miscalculated how fanout would slow down many of my
signals given the plethora of modules and high clock-loads and therefore should have tried
a more pipelined archetecture from the start. Given that operating in real time was not a
condition for my project there is no downside to pipelinning the math and I suggest future
implementors do the same from the beginning if they don’t have a timing constraint.
If a future implementor would like to start from where I left off the next steps would be
to adjust all of my modules to use many more measurements (hopefully from non-deffective
HC-SR04s) to provide a more accurate idea of the θ at which the rover is located and
therefore provide a much more accurate path. Also future implementors should look into
using RF instead of IR communication to allow for back and forth communication which
could allow for more streamlined commands without overly conservative delays by the Labkit
and could potentially provide real-time feedback on the path the “Rover” is taking. If a future
implementor doesn’t want to do real-time feedback even a simple feedback system which can
scale future move commands based on how close the rover gets to the target would be a large
improvement over the current algorithm.
That all said, I have effective one shot path finding working with communcation between a
Nexys4 and a Labkit over IR and Ultrasound based range detection that is all displayed on
a VGA. I am quite happy with my work.
24
Appendix A
Bibliography
25
Appendix B
Verilog Code
https://github.com/plancherb1/6.111-Final-Project
A PDF print of the current codebase as of the date of publication also follows.
26
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// connect our module which will compute the distances for each ultrasound
reg run_hcsr04;
wire hcsr04_done;
wire [4:0] hcsr04_state;
wire [7:0] rover_distance_t;
get_median_of_3_HCSR04_runs gm3hcsr04 (.clock(clock),.reset(reset),.enable(run_hcsr04),
.curr_ultrasound(curr_ultrasound),.ultrasound_response(
ultrasound_response),
.ultrasound_trigger(ultrasound_trigger),.ultrasound_pow
er(ultrasound_power),
.rover_distance(rover_distance_t),.done(hcsr04_done),.s
tate(hcsr04_state));
// fsm parameters
parameter IDLE = 4'h0;
parameter RUN = 4'h1;
parameter PAUSE = 4'h2; // induce a 1 cycle delay to allow the module to get out of the
done state
parameter WAIT = 4'h3;
-1-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// run the helper module to calc the vale for the curr ultrasound
RUN: begin
run_hcsr04 <= 1;
state <= PAUSE;
end
// wait for the helper to finish and then potentially save the value
WAIT: begin
run_hcsr04 <= 0;
if (hcsr04_done) begin
// if this is the new best value save it
if ((best_distance == 0) || (rover_distance_t < best_distance)) begin
best_distance <= rover_distance_t;
best_angle <= (curr_ultrasound << 1) + 1; // occurs at 1,3,5,7,9,11 times
15 degrees for 0,1,2,3,4,5 ultrasound numbers
end
// if done then go to report state
if (curr_ultrasound == TOTAL_ULTRASOUNDS - 1) begin
state <= REPORT;
curr_ultrasound <= 0;
end
// else go to next ultrasound
else begin
curr_ultrasound <= curr_ultrasound + 1;
state <= RUN;
end
end
end
-2-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endcase
end
end
endmodule
module get_median_of_3_HCSR04_runs(
input clock,
input reset,
input enable,
input [3:0] curr_ultrasound, // which ultrasound to run (0 to 5)
input [5:0] ultrasound_response, // can use up to 6 ultrasounds
output [5:0] ultrasound_trigger, // can use up to 6 ultrasounds
output [5:0] ultrasound_power, // can use up to 6 ultrasounds
output reg [7:0] rover_distance,
output reg done,
output reg [3:0] state // exposed for debug
);
-3-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
.curr_ultrasound(curr_ultrasound),.ultrasound_response(ultrasound_respon
se),
.ultrasound_trigger(ultrasound_trigger),.ultrasound_power(ultrasound_pow
er),
.rover_distance(rover_distance_t),.done(hcsr04_done),.state(hcsr04_state
));
-4-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// wait for the HCSR04 to finish and the save the value
WAIT: begin
run_hcsr04 <= 0;
if (hcsr04_done) begin
// save the value in the correct variable
case (repeat_counter)
1: distance_pass_1 <= rover_distance_t;
2: distance_pass_2 <= rover_distance_t;
default: distance_pass_0 <= rover_distance_t;
endcase
// if we are done then move to the next state
if (repeat_counter == NUM_REPEATS - 1) begin
state <= CALC_MEDIAN;
repeat_counter <= 0;
end
// else run again
else begin
state <= RUN;
repeat_counter <= repeat_counter + 1;
end
end
end
endcase
end
end
endmodule
-5-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
module run_HCSR04(
input clock,
input reset,
input enable,
input [3:0] curr_ultrasound, // which ultrasound to run (0 to 5)
input [5:0] ultrasound_response, // can use up to 6 ultrasounds
output reg [5:0] ultrasound_trigger, // can use up to 6 ultrasounds
output reg [5:0] ultrasound_power, // can use up to 6 ultrasounds
output reg [7:0] rover_distance,
output reg done,
output reg [3:0] state // exposed for debug
);
-6-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// count until we see a 0 and then either report the result or powercycle if needed
WAIT_FOR0: begin
// if we see a zero analyze for report
if (~ultrasound_response[curr_ultrasound]) begin
// 148 microsecond per inch means to get inches we divide the count by
// 148*27 = 3996 ~ 4096 so just shift it down 12 times
distance_count <= (distance_count >> 12);
state <= REPORT;
end
// else if we hit max time go to power cycle and report nothing found
else if (distance_count == DISTANCE_MAX - 1) begin
distance_count <= NOTHING_FOUND - DISTANCE_OFFSET;
-7-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
-8-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
power_cycle_timer <= 0;
end
end
endcase
end
end
endmodule
module median_3
(input [19:0] data1,
input [19:0] data2,
input [19:0] data3,
output [19:0] median);
wire min1;
wire max1;
wire comp23;
assign min1 = (data2 > data1) && (data3 > data1);
assign max1 = (data2 < data1) && (data3 < data1);
assign comp23 = data3 > data2;
wire med1;
wire med2;
// if 1 is min or max not 1 else 1
// if 1 is min and 2<3 else if 1 is max and 3<2 then 2
assign med1 = !(min1 || max1);
assign med2 = (min1 && comp23) || (max1 && (!comp23));
endmodule
-9-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
//////////////////////////////////////////////////////////////////////////////////
module vga_writer (
input vclock, // 65MHz clock
input reset, // 1 to initialize module
input [11:0] location, // input location of the Rover
input [4:0] orientation, // orientation of the rover
input [11:0] target_location,// location of the target
input new_data, // ready to re-draw and use the new location
input orientation_ready, // ready to draw the orientation
input [10:0] hcount, // horizontal index of current pixel (0..1023)
input [9:0] vcount, // vertical index of current pixel (0..767)
input hsync, // XVGA horizontal sync signal (active low)
input vsync, // XVGA vertical sync signal (active low)
input blank, // XVGA blanking (1 means output black pixel)
output phsync, // output horizontal sync
output pvsync, // output vertical sync
output pblank, // output blanking
//output analyzer_clock, // debug only
//output [15:0] analyzer_data, // debug only
output reg [23:0] pixel // output pixel // r=23:16, g=15:8, b=7:0
);
// we need to delay hxync, vsync, and blank by the same amount as our
// total pipeline time below
parameter PIPELINE_LENGTH = 5;
delayN #(.NDELAY(PIPELINE_LENGTH)) hdelay (.clk(vclock),.in(hsync),.out(phsync));
delayN #(.NDELAY(PIPELINE_LENGTH)) vdelay (.clk(vclock),.in(vsync),.out(pvsync));
delayN #(.NDELAY(PIPELINE_LENGTH)) bdelay (.clk(vclock),.in(blank),.out(pblank));
-10-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// for debug
//assign analyzer_clock = vsync;
//assign analyzer_data = {rover_x[7:0],rover_y[7:0]};
-11-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
target(.center_x(target_x),.center_y(target_y),.x_value(x_value),.y_value(y_value),.pixel
(target_pixel));
rover_noO(.center_x(rover_x),.center_y(rover_y),.x_value(x_value),.y_value(y_value),.pixe
l(rover_pixel_noO));
rover_yesO(.center_x(rover_x),.center_y(rover_y),.x_value(x_value),.y_value(y_value),.pix
el(rover_pixel_yesO),
.orientation(orientation),.clock(vclock));
// we then pipeline the rest of the VGA display because it takes too long to clear
always @(posedge vclock) begin
// when we reset move the rover off of the screen and wait for ultrasound to update
if (reset) begin
rover_x <= 0;
rover_y <= GRID_BOTTOM_BORDER/2;
end
else begin
// only actually update the position every screen refresh for both the target and the
rover
if (!vsync) begin
// else for the location of the "Rover" we only update when we have valid new
information
if (new_data | orientation_ready) begin
-12-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// UPDATE THE SCALE FACTOR ???? ----- STRETCH GOAL WOULD GO HERE USING MAXX AND MAXY
end
// else enter the pipelined FSM to calculate all of the correct pixel values
else begin
// Get the values back from the helper functions
// grid takes 4 clock cycles so delay 1 for rover combos
// triangle (oriented target) takes 4 clock cycles so delay 0
// blobs (un-oriented rover and target) take 1 cycle so delay 4
// alpha blend takes 1 clock cycle
// final output is delayed then by 5 clock cycles
endmodule
-13-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
-14-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
wire in_square;
assign in_square = (x_value >= (center_x-WIDTH_D2) && x_value < (center_x+WIDTH_D2)) &&
(y_value >= (center_y-HEIGHT_D2) && y_value < (center_y+HEIGHT_D2));
endmodule
module grid
#(parameter BLANK_COLOR = 24'h00_00_00, GRID_COLOR = 24'hFF_00_00,
LEFT_BORDER = -128, RIGHT_BORDER = 128,
TOP_BORDER = 640, BOTTOM_BORDER = 128, LINE_WIDTH = 1)
(input signed [11:0] x_value,
input signed [11:0] y_value,
input clock,
output reg [23:0] pixel);
-15-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
reg signed [31:0] test_15deg; // large bit size to multiply and shift
reg signed [31:0] test_45deg; // large bit size to multiply and shift
reg signed [31:0] test_75deg; // large bit size to multiply and shift
reg [31:0] r_e; // large bit size to multiply
// keep passign the current y_e and borders
reg signed [11:0] y_value_e2;
reg on_border2;
reg out_of_border2;
-16-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
end
endmodule
-17-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// Phase 1 helpers
reg in_square;
reg signed [11:0] delta_x;
reg signed [11:0] delta_y;
reg signed [11:0] abs_delta_x;
reg signed [11:0] abs_delta_y;
reg [31:0] orientation_quadrant; // large bit size to multiply and shift
reg [4:0] orientation2;
// Phase 2 helpers
// while we are solving for 00 to 90 we are really solving for 00 to 90 + 90n to get
// all for directions and then using a quadrant test later
// for 00 and 90 need some x or y and 0 of the other
reg test_on_00;
reg test_on_45;
reg test_on_90;
reg signed [31:0] test_on_15; // large bit size to multiply and shift
reg signed [31:0] test_on_30; // large bit size to multiply and shift
reg signed [31:0] test_on_60; // large bit size to multiply and shift
reg signed [31:0] test_on_75; // large bit size to multiply and shift
reg [1:0] orientation_quadrant2;
reg signed [11:0] delta_x2;
reg signed [11:0] delta_y2;
reg in_square2;
reg [4:0] orientation3;
// Phase 3 helpers
// we need to apply a ROUNDING factor for the bit shift rounding
parameter ROUNDING_FACTOR = 2;
parameter ROUNDING_FACTOR_2 = 2 * ROUNDING_FACTOR;
reg on_00;
-18-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
reg on_15;
reg on_30;
reg on_45;
reg on_60;
reg on_75;
reg on_90;
// keep the quadrant and orientation and in square around
reg right_quadrant;
reg [2:0] orientation_angle;
reg in_square3;
// Phase 2 calc all lines we care about, get angle and make sure we are in right quadrant
test_on_00 <= (!(abs_delta_x == 0)) && (abs_delta_y == 0); // change in x but none in y
test_on_90 <= (abs_delta_x == 0) && (!(abs_delta_y == 0)); // change in y but none in x
test_on_45 <= abs_delta_x == abs_delta_y; // for 45 we need delta x = delta y
test_on_15 <= ((abs_delta_x*17) >>> 6) - abs_delta_y; // tan 15 is about 17/64
test_on_30 <= ((abs_delta_x*37) >>> 6) - abs_delta_y; // tan 75 is about 37/64
test_on_60 <= ((abs_delta_x*111) >>> 6) - abs_delta_y; // tan 75 is about 111/64
test_on_75 <= ((abs_delta_x*240) >>> 6) - abs_delta_y; // tan 75 is about 240/64
// save values for next phase
orientation_quadrant2 <= orientation_quadrant[1:0];
in_square2 <= in_square;
orientation3 <= orientation2;
delta_x2 <= delta_x;
delta_y2 <= delta_y;
// phase 3 find if we are on the lines and in the right quadrant and get the angle
on_00 <= test_on_00;
on_90 <= test_on_90;
on_45 <= test_on_45;
on_15 <= (test_on_15 - ROUNDING_FACTOR <= 0) &&
(test_on_15 + ROUNDING_FACTOR >= 0);
on_30 <= (test_on_30 - ROUNDING_FACTOR <= 0) &&
(test_on_30 + ROUNDING_FACTOR >= 0);
on_60 <= (test_on_60 - ROUNDING_FACTOR <= 0) &&
(test_on_60 + ROUNDING_FACTOR >= 0);
on_75 <= (test_on_75 - ROUNDING_FACTOR_2 <= 0) &&
(test_on_75 + ROUNDING_FACTOR_2 >= 0);
case (orientation_quadrant2)
-19-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
-20-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// default to 00 degrees
default: begin
pixel <= on_00 ? INDICATOR_COLOR : COLOR;
end
endcase
end
else begin
// if in square but not right quadrant then COLOR else BLANK
if (in_square3) begin
pixel <= COLOR;
end
else begin
pixel <= BLANK_COLOR;
end
end
end
endmodule
module alpha_blend
#(parameter ALPHA_M = 2,ALPHA_N = 4,ALPHA_N_LOG_2 = 2)
(input [23:0] pixel_1,
input [23:0] pixel_2,
output [23:0] overlap_pixel);
// show either the alpha blend or the one that exists if they don't overlap
assign overlap_pixel = ((pixel_1 & pixel_2) > 0) ? alpha_blend_pixel : (pixel_1 | pixel_2);
endmodule
-21-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endmodule // delayN
module polar_to_cartesian
(input [11:0] r_theta, // r is [7:0] theta is [11:8]
output reg signed [8:0] x_value, // 1 for sign and 7 for the value as the sin could be a 1
theoretically
output reg signed [8:0] y_value);
// do the math
// sin 15 = cos 75 = sin 165 = - cos 105 // is about 66/256
// sin 30 = cos 60 = sin 150 = - cos 120 // is exactly 1/2
// sin 45 = cos 45 = sin 135 = - cos 135 // is about 181/256
// sin 60 = cos 30 = sin 120 = - cos 150 // is about 222/256
// sin 75 = cos 15 = sin 105 = - cos 165 // is about 247/256
-22-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
-23-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
x_value = NEG*rsin_45deg[7:0];
y_value = POS*rsin_45deg[7:0];
end
4'hA: begin // 150deg
x_value = NEG*rsin_60deg[7:0];
y_value = POS*rsin_30deg[7:0];
end
4'hB: begin // 165deg
x_value = NEG*rsin_75deg[7:0];
y_value = POS*rsin_15deg[7:0];
end
4'hC: begin // 180deg
x_value = NEG*r_theta[7:0];
y_value = ZERO;
end
default: begin // 0deg
x_value = POS*r_theta[7:0];
y_value = ZERO;
end
endcase
end
endmodule
-24-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// on/off parameters
parameter OFF = 1'b0;
parameter ON = 1'b1;
// state parameters
parameter IDLE = 5'h00;
parameter RUN_ULTRASOUND_1 = 5'h01;
parameter ORIENTATION_PHASE_1 = 5'h02;
parameter IR_TRANSMIT_DELAY_1 = 5'h03;
parameter ORIENTATION_MOVE_S = 5'h04;
parameter RUN_ULTRASOUND_2 = 5'h05;
parameter ORIENTATION_PHASE_2 = 5'h06;
parameter ORIENTATION_PHASE_3 = 5'h07;
parameter CALC_MOVE_COMMAND_1 = 5'h08;
parameter CALC_MOVE_COMMAND_2 = 5'h09;
parameter READY_TO_MOVE = 5'h0A;
parameter IR_TRANSMIT_DELAY_2 = 5'h0B;
parameter MOVE_MOVE = 5'h0C;
parameter RUN_ULTRASOUND_3 = 5'h0D;
parameter ARE_WE_DONE = 5'h0F;
parameter ONE_CYCLE_DELAY_1 = 5'h11;
parameter ONE_CYCLE_DELAY_2 = 5'h12;
parameter ONE_CYCLE_DELAY_3 = 5'h13;
parameter ONE_CYCLE_DELAY_4 = 5'h14;
parameter ONE_CYCLE_DELAY_5 = 5'h15;
parameter ONE_CYCLE_DELAY_6 = 5'h16;
parameter ONE_CYCLE_DELAY_X = 5'h1F;
.enable(orientation_helper_enable),.done(orientation_done_t),.reset(reset
),.clock(clock));
// IR Transmit Helpers
-25-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// MOVE_COMMAND_CALC helpers
wire move_command_helper_done;
reg move_command_helper_enable;
path_math pm (.location(rover_location),.target(target_location),
.current_orientation(orientation), .needed_orientation(needed_orientation),
.enable(move_command_helper_enable),.clock(clock),.reset(reset),
.done(move_command_helper_done),.move_command(move_command_t));
// ARE_WE_DONE helpers
wire location_reached_helper_done;
reg location_reached_helper_enable;
wire reached_target_t;
roughly_equal_locations rel
(.clock(clock),.reset(reset),.loc_1(rover_location),.loc_2(target_location),
.done(location_reached_helper_done),.enable(location_reached_hel
per_enable),
.equal(reached_target_t));
-26-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// other resets
reached_target <= OFF;
location_reached_helper_enable <= OFF;
end
else begin
case (state)
// wait for ultrasound to finish then save the location for orientation
RUN_ULTRASOUND_1: begin
run_ultrasound <= OFF;
if (ultrasound_done) begin
state <= ORIENTATION_PHASE_1;
end
end
-27-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// wait for ultrasound to finish then save the location for orientation math phase
RUN_ULTRASOUND_2: begin
run_ultrasound <= OFF;
if (ultrasound_done) begin
state <= ORIENTATION_PHASE_2;
end
end
-28-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// first we need the orientation between the end and the target
CALC_MOVE_COMMAND_1: begin
move_command_helper_enable <= ON;
state <= ONE_CYCLE_DELAY_4;
end
ONE_CYCLE_DELAY_X: begin
move_command <= move_command_t;
state <= READY_TO_MOVE;
end
READY_TO_MOVE: begin
if(run_move) begin
transmit_ir <= ON;
// set the delay for 1 second per angle and distance to travel and
// an additional 1 for the stall in between
move_delay_timer <= move_command[7:0] + move_command[11:8] + 1;
move_delay_inner_timer <= MOVE_DELAY_FACTOR;
state <= IR_TRANSMIT_DELAY_2;
end
end
-29-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
orientation_done <= 0;
end
else begin
move_delay_timer <= move_delay_timer - 1;
move_delay_inner_timer <= MOVE_DELAY_FACTOR;
end
end
else begin
move_delay_inner_timer <= move_delay_inner_timer - 1;
end
end
-30-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// move resets
move_delay_timer <= 32'h0000_0000;
move_delay_inner_timer <= 32'h0000_0000;
move_command_helper_enable <= OFF;
// other resets
reached_target <= OFF;
location_reached_helper_enable <= OFF;
end
end
end
endcase
end
end
endmodule
-31-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
//
// Notes: this relies on only using 6 angles values of 15deg + 30n up to 165
// if you want to use more or different angles you need to update the code
//
//////////////////////////////////////////////////////////////////////////////////
module orientation_math
(input [11:0] r_theta_original, // r is [7:0] theta is [11:8]
input [11:0] r_theta_final, // r is [7:0] theta is [11:8]
input clock,
input enable,
input reset,
output reg done,
output reg [4:0] orientation);
// We need to pipieline this math as there is a lot of it and since we only run this
// once every couple seconds we can do it with states which induces a delay in all
// of the logic (which doesn't matter in this case) and ensures there won't be errors
// total timing is cartesian to polar (1 mul, 1 shift, 1 comp) + convert to delta
// (1 add and cast to 2s compliment so 2 add 1 shift) + calc abs rtan (1 mul,
// 1 shift + 2 add 1 shift) + quad (2 comp) + comps (1 comp + 1 add) + final (1 add)
// = (2 mul, 4 shift, 6 add, 6 comp) but the compiler can't do this all in
// parallel and fan out slows things down with big bit sizes so we will
// use helper modules to do the translation for us in states to solve
// tan theta * delta_x = delta_y
// Helper Angles
parameter DEG360 = 5'h18;
parameter DEG180 = 5'h0C;
// FSM states
reg [3:0] state;
parameter IDLE = 4'h0;
parameter SHORTCUT_TEST = 4'h1;
parameter SHORTCUT_TEST_2 = 4'h2;
parameter PTC = 4'h3;
parameter DELTAS = 4'h4;
parameter ABS_DELTA_QUAD = 4'h5;
parameter DX_TAN = 4'h6;
parameter ABS_DIFF = 4'h7;
parameter BASE_ANGLE_CALC = 4'h8;
parameter CALC_ORIENTATION = 4'h9;
parameter REPORT = 4'hF;
// SHORTCUT HELPERS
reg [4:0] original_base_angle;
reg [4:0] shortcut_quadrant_adjust;
// PTC helpers
reg signed [8:0] x_original;
-32-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// DELTAS helpers
reg signed [8:0] delta_y;
reg signed [8:0] delta_x;
// ABS_DELTA_QUAD helpers
wire [7:0] abs_delta_x_t;
wire [7:0] abs_delta_y_t;
reg [7:0] abs_delta_x;
reg [7:0] abs_delta_y;
reg [1:0] quadrant;
wire [1:0] quadrant_t;
abs_val_8 absx (.v(delta_x),.absv(abs_delta_x_t));
abs_val_8 absy (.v(delta_y),.absv(abs_delta_y_t));
quadrant q1 (.x(delta_x),.y(delta_y),.q(quadrant_t));
// DX_TAN helpers
reg [7:0] abs_dx_tan15;
reg [7:0] abs_dx_tan30;
reg [7:0] abs_dx_tan45;
reg [9:0] abs_dx_tan60;
reg [9:0] abs_dx_tan75;
wire [7:0] abs_dx_tan15_t;
wire [7:0] abs_dx_tan30_t;
wire [7:0] abs_dx_tan45_t;
wire [9:0] abs_dx_tan60_t;
wire [9:0] abs_dx_tan75_t;
//use a helper function for the abs(delta x * theta)
calc_abs7rtan_00_75_15 abstan(.r(abs_delta_x),.abs7rtan_15(abs_dx_tan15_t),
.abs7rtan_30(abs_dx_tan30_t),.abs7rtan_45(abs_dx_tan45_t),
.abs7rtan_60(abs_dx_tan60_t),.abs7rtan_75(abs_dx_tan75_t));
// ABS_DIFF helpers
reg [7:0] diff_15;
reg [7:0] diff_30;
reg [7:0] diff_45;
reg [7:0] diff_60;
reg [7:0] diff_75;
wire [7:0] diff_15_t;
wire [7:0] diff_30_t;
wire [7:0] diff_45_t;
wire [9:0] diff_60_t;
-33-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// BASE_ANGLE helpers
reg [2:0] base_angle;
wire [2:0] base_angle_t;
find_min_5_vals_cascading min5( .input1(diff_15),.input2(diff_30),
.input3(diff_45),.input4(diff_60),
.input5(diff_75),.output_index(base_angle_t));
-34-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// then lets find the quadrant and the abs deltas in x and y
ABS_DELTA_QUAD: begin
abs_delta_x <= abs_delta_x_t;
abs_delta_y <= abs_delta_y_t;
// we can determine quadrant with the following:
// if delta y positive and delta x positive then Q1, both negative Q3 --> tan
positive
// if delta y positive and delta x negative then Q2, inverse Q4 --> tan negative
quadrant <= quadrant_t;
state <= DX_TAN;
end
// we then need to find abs value of the differences between the calcs and delta y
ABS_DIFF: begin
diff_15 <= diff_15_t;
diff_30 <= diff_30_t;
diff_45 <= diff_45_t;
// if bigger than 8 bits then set to max because still bad
diff_60 <= diff_60_t > 8'hFF ? 8'hFE : diff_60_t[7:0]; // make one smaller
-35-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// then use the base angle and quadrant to return the orientation
CALC_ORIENTATION: begin
case(quadrant)
1: orientation <= (DEG180 - base_angle); // 75 = 180-75, 15 = 180-15
2: orientation <= (DEG180 + base_angle); // 15 = 180+15, 75 = 180+75
3: orientation <= (DEG360 - base_angle); // 75 = 360-75, 15 = 360-15
default: orientation <= base_angle; // 15 = 15, 75 = 75
endcase
state <= REPORT;
end
// report out the answer is done and get ready for next math
REPORT: begin
done <= 1;
state <= IDLE;
// make sure to module 24 if needed aka reduce angle to [0 to 360)
if (orientation >= DEG360) begin
orientation <= orientation - DEG360;
end
end
// default to IDLE
default: begin
if (enable) begin
state <= SHORTCUT_TEST;
done <= 0;
end
end
endcase
end
end
endmodule
-36-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// Notes: this relies on only using 6 angles values of 15deg + 30n up to 165
// if you want to use more or different angles you need to update the code
//
//////////////////////////////////////////////////////////////////////////////////
module path_math
(input [11:0] location, // r is [7:0] theta is [11:8]
input [11:0] target, // r is [7:0] theta is [11:8]
input [4:0] current_orientation, // angle = orientation * 15deg
input clock,
input enable,
input reset,
output reg done,
output reg [4:0] needed_orientation, // angle = orientation * 15deg
output reg [11:0] move_command); // distance is [6:0] and angle is [11:7]
// learning from the VGA and orientation I will pipeline this from the start
// our goal is to solve distance of move = delta_y / sin(orientation)
// we also know that angle of move = orientation of move - theta of location
// Helper Angles
parameter DEG360 = 5'h18;
parameter DEG180 = 5'h0C;
// FSM states
reg [3:0] state;
parameter IDLE = 4'h0;
parameter NEEDED_ORIENTATION_1 = 4'h1;
parameter ONE_CYCLE_DELAY_1 = 4'h2;
parameter NEEDED_ORIENTATION_2 = 4'h3;
parameter ONE_CYCLE_DELAY_2 = 4'h4;
parameter PTC_AND_ANGLE = 4'h5;
parameter DELTAS = 4'h6;
parameter ABS_DELTA_QUAD = 4'h7;
parameter ORIENT_BASE_ANGLE = 4'h8;
parameter ABS_DY_DIV_SIN = 4'h9;
parameter REPORT = 4'hF;
reg orientation_helper_enable;
wire [4:0] orientation_t;
wire orientation_done;
orientation_math om
(.r_theta_original(location),.r_theta_final(target),.orientation(orientation_t),
.enable(orientation_helper_enable),.done(orientation_done),.reset(reset),
.clock(clock));
// PTC_AND_ANGLE helpers
reg [4:0] angle;
reg signed [8:0] x_location;
reg signed [8:0] y_location;
reg signed [8:0] x_target;
reg signed [8:0] y_target;
-37-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// DELTAS helpers
reg signed [8:0] delta_y;
reg signed [8:0] delta_x;
// ABS_DELTA_QUAD helpers
wire [7:0] abs_delta_x_t;
wire [7:0] abs_delta_y_t;
reg [7:0] abs_delta_x;
reg [7:0] abs_delta_y;
reg [1:0] quadrant;
wire [1:0] quadrant_t;
abs_val_8 absx (.v(delta_x),.absv(abs_delta_x_t));
abs_val_8 absy (.v(delta_y),.absv(abs_delta_y_t));
quadrant q1 (.x(delta_x),.y(delta_y),.q(quadrant_t));
// ORIENT_BASE_ANGLE helpers
reg [2:0] base_angle;
// ABS_DY_DIV_SIN helpers
reg [6:0] distance;
wire [7:0] distance_t;
//use a helper function for the math
calc_r_y_theta calcr (.y(abs_delta_y),.x(abs_delta_x),.theta(base_angle),.r(distance_t));
-38-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
case(state)
NEEDED_ORIENTATION_2: begin
orientation_helper_enable <= 0;
// if the helper is done save the value
if (orientation_done) begin
needed_orientation <= orientation_t;
state <= ONE_CYCLE_DELAY_2;
end
end
// then lets find the quadrant and the abs deltas in x and y
ABS_DELTA_QUAD: begin
abs_delta_x <= abs_delta_x_t;
abs_delta_y <= abs_delta_y_t;
// we can determine quadrant with the following:
// if delta y positive and delta x positive then Q1, both negative Q3 --> tan
positive
// if delta y positive and delta x negative then Q2, inverse Q4 --> tan negative
quadrant <= quadrant_t;
state <= ORIENT_BASE_ANGLE;
end
-39-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
case (quadrant)
1: base_angle <= (DEG180 - needed_orientation);
2: base_angle <= (needed_orientation - DEG180);
3: base_angle <= (DEG360 - needed_orientation);
default: base_angle <= needed_orientation[3:0];
endcase
state <= ABS_DY_DIV_SIN;
end
// report out the answer is done and get ready for next math
REPORT: begin
done <= 1;
state <= IDLE;
move_command <= {angle,distance};
end
// default to IDLE
default: begin
if (enable) begin
state <= NEEDED_ORIENTATION_1;
done <= 0;
orientation_helper_enable <= 0;
needed_orientation <= 0;
end
end
endcase
end
end
endmodule
-40-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endmodule
// fsm helpers
reg [3:0] state;
parameter IDLE = 4'h0;
parameter PTC = 4'h1;
parameter DELTAS = 4'h2;
parameter D_2 = 4'h3;
parameter COMP = 4'h4;
-41-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// PTC helpers
reg signed [8:0] loc_1_x;
reg signed [8:0] loc_1_y;
reg signed [8:0] loc_2_x;
reg signed [8:0] loc_2_y;
wire signed [8:0] loc_1_x_t;
wire signed [8:0] loc_1_y_t;
wire signed [8:0] loc_2_x_t;
wire signed [8:0] loc_2_y_t;
polar_to_cartesian ptc_original (.r_theta(loc_1),.x_value(loc_1_x_t),.y_value(loc_1_y_t));
polar_to_cartesian ptc_final (.r_theta(loc_2),.x_value(loc_2_x_t),.y_value(loc_2_y_t));
// COMP helpers
parameter MAX_DISTANCE_FOR_EQUAL = 6;
parameter MAX_DISTANCE_FOR_EQUAL_2 = MAX_DISTANCE_FOR_EQUAL * MAX_DISTANCE_FOR_EQUAL;
-42-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
default: begin
equal <= 0;
done <= 0;
if (enable) begin
state <= PTC;
end
end
endcase
end
end
endmodule
module abs_val_8
(input signed [8:0] v,
output wire [7:0] absv);
endmodule
-43-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endmodule
module calc_abs7rtan_00_75_15
(input [7:0] r,
output wire [7:0] abs7rtan_15,
output wire [7:0] abs7rtan_30,
output wire [7:0] abs7rtan_45,
output wire [9:0] abs7rtan_60,
output wire [9:0] abs7rtan_75);
-44-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endmodule
module abs_diff_7(
input [7:0] x,
input [7:0] y,
output [7:0] absdiff
);
endmodule
module abs_diff_9(
input [9:0] x,
input [9:0] y,
output [9:0] absdiff
);
endmodule
-45-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
wire comp1_2;
wire comp2_3;
wire comp3_4;
wire comp4_5;
assign comp1_2 = input1 >= input2;
assign comp2_3 = input2 >= input3;
assign comp3_4 = input3 >= input4;
assign comp4_5 = input4 >= input5;
wire is1;
wire is5;
wire is2;
wire is4;
// if they are cascading smaller than 15 is the smallest
assign is1 = ((!comp1_2) & (!comp2_3) & (!comp3_4) & (!comp4_5));
// if they are cascading bigger than 75 is the smallest
assign is5 = (comp1_2 & comp2_3 & comp3_4 & comp4_5);
// if not 15 or 75 than can do same for cascading smaller for 30
assign is2 = ((!is1) & (!is5) & (!comp2_3) & (!comp3_4));
// if not 15 or 75 than can do same for cascading bigger for 60
assign is4 = ((!is1) & (!is5) & (comp2_3) & (comp3_4));
assign output_index = is1 ? 3'h1 : (is5 ? 3'h5 : (is2 ? 3'h2 : (is4 ? 3'h4 : 3'h3)));
endmodule
-46-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// if 0 then all x
// 1/sin 15 is about 989/256 ~ 4
// 1/sin 30 is exactly 2
// 1/sin 45 is about 362/256 ~ 2
// 1/sin 60 is about 296/256 ~ 1
// 1/sin 75 is about 265/256 ~ 1
// if 90 then all y
wire [31:0] r_15deg; // large bit size to multiply and shift
wire [31:0] r_30deg; // large bit size to multiply and shift
wire [31:0] r_45deg; // large bit size to multiply and shift
wire [31:0] r_60deg; // large bit size to multiply and shift
wire [31:0] r_75deg; // large bit size to multiply and shift
assign r_15deg = (y*989) >> 10;
assign r_30deg = y >> 9;
assign r_45deg = (y*362) >> 10;
assign r_60deg = (y*296) >> 10;
assign r_75deg = (y*265) >> 10;
endmodule
-47-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
///////////////////////////////////////////////////////////////////////////////////////
//
// here we count the number of "ones" in the signal, subtract from wait time
// and pad the wait state to start the next command sequence exactly 45ms later.
wire [3:0] sum_ones = address[4] + address[3] + address[2] + address[1] + address[0] +
command[6] + command[5] + command[4] + command[3] + command[2] + command[1] +
command[0];
wire[9:0] WAIT_TO_45MS = 10'd376 - (sum_ones*8);
//
///////////////////////////////////////////////////////////////////////////////////////
wire start_timer;
wire expired;
timer #(.COUNT_GOAL(COUNT_GOAL))
t (.clk(clk),
-48-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
.reset(reset),
.start_timer(start_timer),
.length(timer_length),
.expired(expired));
always@(posedge clk)
begin
// signal modulation
mod_count <= (mod_count == 674) ? 0 : mod_count + 1; // was 1349
if (reset)
state <= IDLE;
else begin
if (state == START)
begin
cur_bit <= 0;
cur_value <= value;
end
// when a bit finishes being transmitted, left shift cur_value
// so that the next bit can be transmitted, and increment cur_bit
if (state == BIT && next_state == TRANS)
begin
cur_bit <= cur_bit + 1;
cur_value <= {1'b0, cur_value[11:1]};
end
state <= next_state;
end
end
always@*
begin
case(state)
IDLE: next_state = transmit ? WAIT : IDLE;
WAIT: next_state = expired ? (transmit ? START : IDLE) : WAIT;
START: next_state = expired ? TRANS : START;
TRANS: next_state = expired ? BIT : TRANS;
BIT : next_state = expired ? (cur_bit == 11 ? WAIT : TRANS) : BIT;
default: next_state = IDLE;
endcase
end
// always start the timer on a state transition
assign start_timer = (state != next_state);
assign timer_length = (next_state == WAIT) ? WAIT_TO_45MS : // was 63; 600-4-24-6 = 566
(next_state == START) ? 10'd32 :
(next_state == TRANS) ? 10'd8 :
(next_state == BIT ) ? (cur_value[0] ? 10'd16 : 10'd8 ) : 10'd0;
assign signal_out = ((state == START) || (state == BIT)) && (mod_count < 169); // was 338
gph
endmodule
-49-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// after 'length' 75us intervals have passed. e.g. if length is 10, timer will
// assert expired after 750us.
// Updated November 1, 2015 - added in parameter for multiple clock driving output (Brian
Plancher)
///////////////////////////////////////////////////////////////////////////////
module timer #(parameter COUNT_GOAL=2024) // set for 27mhz clock (for 25mhz clock use 1875)
(input wire clk,
input wire reset,
input wire start_timer,
input wire [9:0] length,
output wire expired);
wire enable;
divider_600us #(.COUNT_GOAL(COUNT_GOAL)) sc
(.clk(clk),.reset(start_timer),.enable(enable));
reg [9:0] count_length;
reg [9:0] count;
reg counting;
always@(posedge clk)
begin
if (reset)
counting <= 0;
else if (start_timer)
begin
count_length <= length;
count <= 0;
counting <= 1;
end
else if (counting && enable)
count <= count + 1;
else if (expired)
counting <= 0;
end
-50-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
////////////////////////////////////////////////////////////////////////////
//
// Display Clock
//
// Generate a 500kHz clock for driving the displays.
//
////////////////////////////////////////////////////////////////////////////
-51-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
////////////////////////////////////////////////////////////////////////////
//
// Display State Machine
//
////////////////////////////////////////////////////////////////////////////
8'h01:
begin
// End reset
disp_reset_b <= 1'b1;
state <= state+1;
end
8'h02:
begin
// Initialize dot register (set all dots to zero)
disp_ce_b <= 1'b0;
disp_data_out <= 1'b0; // dot_index[0];
if (dot_index == 639)
state <= state+1;
else
-52-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
8'h03:
begin
// Latch dot data
disp_ce_b <= 1'b1;
dot_index <= 31; // re-purpose to init ctrl reg
disp_rs <= 1'b1; // Select the control register
state <= state+1;
end
8'h04:
begin
// Setup the control register
disp_ce_b <= 1'b0;
disp_data_out <= control[31];
control <= {control[30:0], 1'b0}; // shift left
if (dot_index == 0)
state <= state+1;
else
dot_index <= dot_index-1;
end
8'h05:
begin
// Latch the control register data / dot data
disp_ce_b <= 1'b1;
dot_index <= 39; // init for single char
char_index <= 15; // start with MS char
state <= state+1;
disp_rs <= 1'b0; // Select the dot register
end
8'h06:
begin
// Load the user's dot data into the dot reg, char by char
disp_ce_b <= 1'b0;
disp_data_out <= dots[dot_index]; // dot data from msb
if (dot_index == 0)
if (char_index == 0)
state <= 5; // all done, latch data
else
begin
char_index <= char_index - 1; // goto next char
dot_index <= 39;
end
else
dot_index <= dot_index-1; // else loop thru all dots
end
endcase
-53-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
always @(nibble)
case (nibble)
4'h0: dots <= 40'b00111110_01010001_01001001_01000101_00111110 ;
4'h1: dots <= 40'b00000000_01000010_01111111_01000000_00000000 ;
4'h2: dots <= 40'b01100010_01010001_01001001_01001001_01000110 ;
4'h3: dots <= 40'b00100010_01000001_01001001_01001001_00110110 ;
4'h4: dots <= 40'b00011000_00010100_00010010_01111111_00010000 ;
4'h5: dots <= 40'b00100111_01000101_01000101_01000101_00111001 ;
4'h6: dots <= 40'b00111100_01001010_01001001_01001001_00110000 ;
4'h7: dots <= 40'b00000001_01110001_00001001_00000101_00000011 ;
4'h8: dots <= 40'b00110110_01001001_01001001_01001001_00110110 ;
4'h9: dots <= 40'b00000110_01001001_01001001_00101001_00011110 ;
4'hA: dots <= 40'b01111110_00001001_00001001_00001001_01111110 ;
4'hB: dots <= 40'b01111111_01001001_01001001_01001001_00110110 ;
4'hC: dots <= 40'b00111110_01000001_01000001_01000001_00100010 ;
4'hD: dots <= 40'b01111111_01000001_01000001_01000001_00111110 ;
4'hE: dots <= 40'b01111111_01001001_01001001_01001001_01000001 ;
4'hF: dots <= 40'b01111111_00001001_00001001_00001001_00000001 ;
endcase
endmodule
-54-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
input clock,
input reset,
output reg out
);
reg prev_in;
endmodule
endmodule
-55-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
`default_nettype none
///////////////////////////////////////////////////////////////////////////////
//
// 6.111 FPGA Labkit -- Template Toplevel Module
//
// For Labkit Revision 004
//
//
// Created: October 31, 2004, from revision 003 file
// Author: Nathan Ickes
//
///////////////////////////////////////////////////////////////////////////////
//
// CHANGES FOR BOARD REVISION 004
//
// 1) Added signals for logic analyzer pods 2-4.
// 2) Expanded "tv_in_ycrcb" to 20 bits.
// 3) Renamed "tv_out_data" to "tv_out_i2c_data" and "tv_out_sclk" to
// "tv_out_i2c_clock".
// 4) Reversed disp_data_in and disp_data_out signals, so that "out" is an
// output of the FPGA, and "in" is an input.
//
// CHANGES FOR BOARD REVISION 003
//
// 1) Combined flash chip enables into a single signal, flash_ce_b.
//
// CHANGES FOR BOARD REVISION 002
//
// 1) Added SRAM clock feedback path input and output
// 2) Renamed "mousedata" to "mouse_data"
// 3) Renamed some ZBT memory signals. Parity bits are now incorporated into
// the data bus, and the byte write enables have been combined into the
// 4-bit ram#_bwe_b bus.
// 4) Removed the "systemace_clock" net, since the SystemACE clock is now
// hardwired on the PCB to the oscillator.
//
///////////////////////////////////////////////////////////////////////////////
//
// Complete change history (including bug fixes)
//
// 2006-Mar-08: Corrected default assignments to "vga_out_red", "vga_out_green"
// and "vga_out_blue". (Was 10'h0, now 8'h0.)
//
// 2005-Sep-09: Added missing default assignments to "ac97_sdata_out",
// "disp_data_out", "analyzer[2-3]_clock" and
// "analyzer[2-3]_data".
//
// 2005-Jan-23: Reduced flash address bus to 24 bits, to match 128Mb devices
// actually populated on the boards. (The boards support up to
// 256Mb devices, with 25 address lines.)
//
-56-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
clock_feedback_out, clock_feedback_in,
-57-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
switch,
led,
daughtercard,
analyzer1_data, analyzer1_clock,
analyzer2_data, analyzer2_clock,
analyzer3_data, analyzer3_clock,
analyzer4_data, analyzer4_clock);
input clock_feedback_in;
output clock_feedback_out;
-58-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
////////////////////////////////////////////////////////////////////////////
//
// I/O Assignments
//
////////////////////////////////////////////////////////////////////////////
// VGA Output
//assign vga_out_red = 8'h0;
//assign vga_out_green = 8'h0;
//assign vga_out_blue = 8'h0;
//assign vga_out_sync_b = 1'b1;
//assign vga_out_blank_b = 1'b1;
//assign vga_out_pixel_clock = 1'b0;
//assign vga_out_hsync = 1'b0;
//assign vga_out_vsync = 1'b0;
// Video Output
-59-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// Video Input
assign tv_in_i2c_clock = 1'b0;
assign tv_in_fifo_read = 1'b0;
assign tv_in_fifo_clock = 1'b0;
assign tv_in_iso = 1'b0;
assign tv_in_reset_b = 1'b0;
assign tv_in_clock = 1'b0;
assign tv_in_i2c_data = 1'bZ;
// tv_in_ycrcb, tv_in_data_valid, tv_in_line_clock1, tv_in_line_clock2,
// tv_in_aef, tv_in_hff, and tv_in_aff are inputs
// SRAMs
assign ram0_data = 36'hZ;
assign ram0_address = 19'h0;
assign ram0_adv_ld = 1'b0;
assign ram0_clk = 1'b0;
assign ram0_cen_b = 1'b1;
assign ram0_ce_b = 1'b1;
assign ram0_oe_b = 1'b1;
assign ram0_we_b = 1'b1;
assign ram0_bwe_b = 4'hF;
assign ram1_data = 36'hZ;
assign ram1_address = 19'h0;
assign ram1_adv_ld = 1'b0;
assign ram1_clk = 1'b0;
assign ram1_cen_b = 1'b1;
assign ram1_ce_b = 1'b1;
assign ram1_oe_b = 1'b1;
assign ram1_we_b = 1'b1;
assign ram1_bwe_b = 4'hF;
assign clock_feedback_out = 1'b0;
// clock_feedback_in is an input
// Flash ROM
assign flash_data = 16'hZ;
assign flash_address = 24'h0;
assign flash_ce_b = 1'b1;
assign flash_oe_b = 1'b1;
assign flash_we_b = 1'b1;
assign flash_reset_b = 1'b0;
assign flash_byte_b = 1'b1;
// flash_sts is an input
-60-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// RS-232 Interface
assign rs232_txd = 1'b1;
assign rs232_rts = 1'b1;
// rs232_rxd and rs232_cts are inputs
// PS/2 Ports
// mouse_clock, mouse_data, keyboard_clock, and keyboard_data are inputs
/*
// LED Displays
assign disp_blank = 1'b1;
assign disp_clock = 1'b0;
assign disp_rs = 1'b0;
assign disp_ce_b = 1'b1;
assign disp_reset_b = 1'b0;
assign disp_data_out = 1'b0;
// disp_data_in is an input
*/
// User I/Os
//assign user1 = 32'hZ;
assign user2 = 32'hZ;
assign user3 = 32'hZ;
assign user4 = 32'hZ;
// Daughtercard Connectors
assign daughtercard = 44'hZ;
// Logic Analyzer
assign analyzer1_data = 16'h0;
assign analyzer1_clock = 1'b1;
assign analyzer2_data = 16'h0;
assign analyzer2_clock = 1'b1;
assign analyzer3_data = 16'h0;
assign analyzer3_clock = 1'b1;
assign analyzer4_data = 16'h0;
assign analyzer4_clock = 1'b1;
-61-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
////////////////////////////////////////////////////////////////////////////
//
// Reset Generation
//
// A shift register primitive is used to generate an active-high reset
// signal that remains high for 16 clock cycles after configuration finishes
// and the FPGA's internal clocks begin toggling.
//
////////////////////////////////////////////////////////////////////////////
wire reset_init;
SRL16 reset_sr(.D(1'b0), .CLK(clock_27mhz), .Q(reset_init),
.A0(1'b1), .A1(1'b1), .A2(1'b1), .A3(1'b1));
defparam reset_sr.INIT = 16'hFFFF;
-62-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
.clean(db_switch[3]));
//debounce #(.DELAY(270000)) db_S4 (.reset(reset), .clock(clock_27mhz), .noisy(switch[4]),
.clean(db_switch[4]));
//debounce #(.DELAY(270000)) db_S5 (.reset(reset), .clock(clock_27mhz), .noisy(switch[5]),
.clean(db_switch[5]));
//debounce #(.DELAY(270000)) db_S6 (.reset(reset), .clock(clock_27mhz), .noisy(switch[6]),
.clean(db_switch[6]));
//debounce #(.DELAY(270000)) db_S7 (.reset(reset), .clock(clock_27mhz), .noisy(switch[7]),
.clean(db_switch[7]));
////////////////////////////////////////////////////////////////////////////
//
// Set up the XVGA Output per Lab3
//
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//
// Connect the Modules to Produce the Desired Behaviors
//
////////////////////////////////////////////////////////////////////////////
-63-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
-64-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
.orient_location_2(orient_location_2),
.needed_orientation(needed_orientation),
.move_command_t(move_command_t),
.state(main_state));
// Ultrasound Block
wire [3:0] ultrasound_state;
wire [3:0] curr_ultrasound;
wire run_ultrasound_manual;
edge_detect e3 (.in(btn1_db),.clock(clock_27mhz),.reset(reset),.out(run_ultrasound_manual));
assign run_ultrasound = run_ultrasound_fsm | run_ultrasound_manual;
rover_location_calculator rlc1 (.clock(clock_27mhz),.reset(reset),.enable(run_ultrasound),
.ultrasound_response(ultrasound_response),
.ultrasound_trigger(ultrasound_trigger),
.ultrasound_power(ultrasound_power),
.rover_location(rover_location),
.done(ultrasound_done),
.state(ultrasound_state),
.curr_ultrasound(curr_ultrasound));
wire ir_manual;
assign ir_manual = btn0_db;
wire run_ir;
assign run_ir = transmit_ir | ir_manual;
// Transmitter (from Lab5b hijacked to send IR)
ir_transmitter transmitter (.clk(clock_27mhz),
.reset(reset),
.address(move_command[11:7]), // angle
.command(move_command[6:0]), // distance
.transmit(run_ir),
.signal_out(ir_signal));
3'h0,main_state, // 5 bits
//3'h0,needed_orientation, // 5 bits
-65-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
//ultrasound_state, // 4 bits
//curr_ultrasound, // 4 bits
3'b0,rover_orientation, // 8
bits
//3'b0, orientation_done, // 4 bits
rover_location,// 12 bits
target_location, // 12 bits
//orient_location_1, // 12 bits
//orient_location_2, // 12 bits
//3'b0, transmit_ir,
//3'b0, reached_target,
move_command_t,//12bits
move_command // 12 bits
};
end
endmodule
// basic parameters
parameter ACTIVE = 1'b1;
parameter IDLE = 1'b0;
-66-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// fsm controls
parameter STATE_WAIT_START = 4'h0;
parameter STATE_COMMAND = 4'h1;
parameter STATE_WAIT_1 = 4'h2;
// set up debug
// assign analyzer_clock = enable;
// assign analyzer_data = {16'h0000};
// synchronize on clock
always @(posedge clock) begin
// if we see reset update all to default
if (reset == ACTIVE) begin
move_data <= 12'h000;
done <= 0;
bits_seen <= 0;
positive_samples <= 0;
state <= STATE_WAIT_START;
end
// else enter states
else begin
case (state)
// between the command we go into state wait for 1 and just ignore all 0s
STATE_WAIT_1: begin
if (data_in == ACTIVE) begin
positive_samples <= 1;
state <= STATE_COMMAND;
end
end
-67-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
if (enable) begin
// if we see a 0 test the stream for a valid high or low
if (data_in == IDLE) begin
// if in threshold for high add a high
if ((positive_samples >= (HIGH_PULSES - HIGH_THRESHOLD)) &&
(positive_samples <= (HIGH_PULSES + HIGH_THRESHOLD))) begin
// add a high and get ready for next signal
move_data[bits_seen] <= 1'b1;
positive_samples <= 0;
// if we have seen all of the bits then notify to done and wait for
next command
if (bits_seen == COMMAND_BITS - 1) begin
state <= STATE_WAIT_START;
bits_seen <= 0;
done <= 1;
end
// else stay in this state
else begin
bits_seen <= bits_seen + 1;
state <= STATE_WAIT_1;
end
end
// else if in threshold for low add a low
else if ((positive_samples >= (LOW_PULSES - LOW_THRESHOLD)) &&
(positive_samples <= (LOW_PULSES + LOW_THRESHOLD))) begin
// add a low and get ready for next signal
move_data[bits_seen] <= 1'b0;
positive_samples <= 0;
// if we have seen all of the bits then notify to done and wait for
next command
if (bits_seen == COMMAND_BITS - 1) begin
state <= STATE_WAIT_START;
bits_seen <= 0;
done <= 1;
end
// else stay in this state
else begin
bits_seen <= bits_seen + 1;
state <= STATE_WAIT_1;
end
end
//else bad data so reset
else begin
positive_samples <= 0;
bits_seen <= 0;
state <= STATE_WAIT_START;
end
end
// else keep counting 1s
else begin
positive_samples <= positive_samples + 1;
end
end
-68-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
end
///////////////////////////////////////////////////////////////////////////////
// enable goes high every 75us, providing 8x oversampling for
// 600us width signal (parameter: 27mhz clock: 2024, 25mhz clock: 1875)
// Updated November 1, 2015 - added in parameter for multiple clock driving output (Brian
Plancher)
///////////////////////////////////////////////////////////////////////////////
module divider_600us #(parameter COUNT_GOAL=2024)
(input wire clk,
input wire reset,
output wire enable);
always@(posedge clk)
begin
if (reset)
count <= 0;
else if (count == COUNT_GOAL)
count <= 0;
else
count <= count + 1;
end
assign enable = (count == COUNT_GOAL);
-69-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endmodule
parameter ON = 1'b1;
parameter OFF = 1'b0;
-70-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endcase
end
end
endmodule
-71-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
// synchronize on clock
always @(posedge clock) begin
// if we see reset update all to default
if (reset == ON) begin
state <= IDLE;
motor_l_f <= OFF;
motor_r_f <= OFF;
motor_l_b <= OFF;
motor_r_b <= OFF;
angle <= 0;
angle_count <= 0;
angle_sub_count <= 0;
distance <= 0;
distance_count <= 0;
distance_sub_count <= 0;
pause_count <= 0;
move_done <= 0;
angle_sub_goal <= FIFTEEN_DEG+(1000000*adjustors[3:0]);
distance_sub_goal <= FOUR_INCHES+(1000000*adjustors[7:4]);
end
// else enter states
else begin
case (state)
-72-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
else begin
angle_count <= angle_count + 1;
angle_sub_count <= 0;
end
end
else begin
angle_sub_count <= angle_sub_count + 1;
end
end
// for test use the code below to simply move for 2500000 clock cycles times
// the value passed in from move command which is simply 0.1 second increments
TESTING_DELAY: begin
if (test_counter == command - 1) begin
motor_l_f <= OFF;
-73-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
-74-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endcase
end
end
endmodule
-75-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
3'b001: begin
seg <= segments[data[27:24]];
strobe <= 8'b1011_1111 ;
end
3'b010: begin
seg <= segments[data[23:20]];
strobe <= 8'b1101_1111 ;
end
3'b011: begin
seg <= segments[data[19:16]];
strobe <= 8'b1110_1111;
end
3'b100: begin
seg <= segments[data[15:12]];
strobe <= 8'b1111_0111;
end
3'b101: begin
seg <= segments[data[11:8]];
strobe <= 8'b1111_1011;
end
3'b110: begin
seg <= segments[data[7:4]];
strobe <= 8'b1111_1101;
end
3'b111: begin
seg <= segments[data[3:0]];
strobe <= 8'b1111_1110;
end
endcase
end
-76-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
endmodule
// pulse synchronizer
module synchronize #(parameter NSYNC = 2) // number of sync flops. must be >= 2
(input clk,in,
output reg out);
module labkit(
input CLK100MHZ,
input[15:0] SW,
input BTNC, BTNU, BTNL, BTNR, BTND,
output[3:0] VGA_R,
output[3:0] VGA_B,
output[3:0] VGA_G,
inout[7:0] JA,
output VGA_HS,
output VGA_VS,
output[15:0] LED,
output[7:0] SEG, // segments A-G (0-6), DP (7)
output[7:0] AN // Display 0-7
);
-77-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
//////////////////////////////////////////////////////////////////////////////////
// create 25mhz system clock
wire clock_25mhz;
clock_quarter_divider clockgen(.clk100_mhz(CLK100MHZ), .clock_25mhz(clock_25mhz));
//////////////////////////////////////////////////////////////////////////////////
// debounce and synchronize all switches and buttons
//////////////////////////////////////////////////////////////////////////////////
-78-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
ir_receiver ir1(.clock(clock_25mhz),.reset(reset),.data_in(ir_in),.done(move_ready),
.move_data(move_data_t),.state(ir_state));
-79-
C:\Users\Brian\Documents\MIT\6.111\Final Project\all_code_for_print.v Tuesday, December 8, 2015 11:30 PM
reg counter = 0;
-80-