Last active
August 7, 2021 23:16
-
-
Save newhouseb/4cf7dfb7bf1e0b6adf80005c6c6d9f39 to your computer and use it in GitHub Desktop.
2GHz Gray Code Oscillator/Counter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is Gray Code oscillator that can be used on Lattice ECP5 devices to measure time down to around 500ps of | |
# precision (with no manual placement!) using only two (!) slices. | |
# | |
# In essence how this works is that we create a self-clocking counter that runs as fast as the FPGA fabric will | |
# allow. If we were to do this with a traditional counter, we would have to worry about propagation delay because | |
# multiple bits change at a time. If you use gray-codes, however, only one bit is changing at any given time so | |
# there are no issues with racing signals. | |
# | |
# If you drop this in a design and wire out the highest bit of the async_gray_count signal into a spectrum | |
# analyzer, you will see a dominant frequency of about 125Mhz. In other words, we cycle through all 16 gray codes | |
# in about 8 nanoseconds, or 500 picoseconds per step (because of routing, these steps aren't going to be uniform). | |
# | |
# What happens if you want to measure time beyond 8 nanoseconds? Well I suppose you could feed the highest bit to | |
# another counter for more signiciant digits. You would probably want to make that a gray-code counter as well. | |
# Reset logic is another outstanding question -- we might be able to squeeze it into the existing LUTs without | |
# reducing the number of bits used to count, but I need to look more at TRELLIS_SLICE. | |
# | |
# Also worth noting, you can likely get things to run even faster if you manually place the two LUTs next to each | |
# other user a_LOC attributes. | |
class AsynchronousGrayCodeCounter(Elaboratable): | |
def __init__(self): | |
self.async_gray_count = Signal(4) | |
self.latched_gray_count = Signal(4) | |
def elaborate(self, platform): | |
m = Module() | |
# This is the sequence of 4-bit Reflected Binary Gray Code | |
sequence = [ | |
0b0000, | |
0b0001, | |
0b0011, | |
0b0010, | |
0b0110, | |
0b0111, | |
0b0101, | |
0b0100, | |
0b1100, | |
0b1101, | |
0b1111, | |
0b1110, | |
0b1010, | |
0b1011, | |
0b1001, | |
0b1000 | |
] | |
maps = [] | |
for idx in range(4): | |
logicmap = [0]*len(sequence) | |
for i in range(len(sequence)): | |
logicmap[sequence[i]] = 1 if sequence[(i + 1) % len(sequence)] & (1 << idx) else 0 | |
maps.append(logicmap) | |
def pack(m): | |
return sum([m[i] << i for i in range(len(m))]) | |
m.submodules.lutA = Instance("TRELLIS_SLICE", | |
p_MODE="LOGIC", | |
p_REG0_SD="1", | |
p_REG1_SD="1", | |
p_LUT0_INITVAL=pack(maps[0]), | |
i_A0=self.async_gray_count[0], | |
i_B0=self.async_gray_count[1], | |
i_C0=self.async_gray_count[2], | |
i_D0=self.async_gray_count[3], | |
o_F0=self.async_gray_count[0], | |
i_DI0=self.async_gray_count[0], | |
o_Q0=self.latched_gray_count[0], | |
p_LUT1_INITVAL=pack(maps[1]), | |
i_A1=self.async_gray_count[0], | |
i_B1=self.async_gray_count[1], | |
i_C1=self.async_gray_count[2], | |
i_D1=self.async_gray_count[3], | |
o_F1=self.async_gray_count[1], | |
i_DI1=self.async_gray_count[1], | |
o_Q1=self.latched_gray_count[1], | |
i_CLK=ClockSignal() | |
) | |
m.submodules.lutB = Instance("TRELLIS_SLICE", | |
p_MODE="LOGIC", | |
p_REG0_SD="1", | |
p_REG1_SD="1", | |
p_LUT0_INITVAL=pack(maps[2]), | |
i_A0=self.async_gray_count[0], | |
i_B0=self.async_gray_count[1], | |
i_C0=self.async_gray_count[2], | |
i_D0=self.async_gray_count[3], | |
o_F0=self.async_gray_count[2], | |
i_DI0=self.async_gray_count[2], | |
o_Q0=self.latched_gray_count[2], | |
p_LUT1_INITVAL=pack(maps[3]), | |
i_A1=self.async_gray_count[0], | |
i_B1=self.async_gray_count[1], | |
i_C1=self.async_gray_count[2], | |
i_D1=self.async_gray_count[3], | |
o_F1=self.async_gray_count[3], | |
i_DI1=self.async_gray_count[3], | |
o_Q1=self.latched_gray_count[3], | |
i_CLK=ClockSignal() | |
) | |
return m |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment