Friday, March 4, 2011

Clock tree synthesis (CTS): ASIC/ASSP vs. FPGA

First time learning experience for me.

Muxing clocks is nothing new to me, I've done this many times. But until now, I've never had to keep both sides synchronous with each other.

process(clkA, clkB, clkSel)
begin
 clkAB <= clkA;
 if(clkSel = '1') then
  clkAB <= clkB;
 end if;
end process;

process(clkA)
begin
 if(clkA'event and clkA = '1') then
  ffA <= sig_tmpA;
 end if;
end process;

process(clkB)
begin
 if(clkB'event and clkB = '1') then
  ffB <= sig_tmpB;
 end if;
end process;

process(clkAB)
begin
 if(clkAB'event and clkAB = '1') then
  if(grabA)
   ffAB <= ffA;
  end if;
  elsif(grabB)
   ffAB <= ffB;
  end if;
 end if;
end process;

Here's the problem. I want ffAB to be set to the value of ffA when grabA is true and I want ffAB to be set to the value of ffB when grabB is true. This requires having clkAB in phase with clkA when clkSel is 0 and having clkAB in phase with clkB when clkSel is 1. The problem is clock skew. Basically the clkAB clock becomes a delayed version of the source clock. This means that the flip-flops before the clock will change before the flip-flop after the clock mux is able to sample the value. This is a standard hold-time problem. For an ASIC/ASSP, the solution is implemented by the synthesis tools. It is pretty automatic from what I've been led to believe. Perhaps some constraints or switches are required, but in general, if the two clocks are related then the tools will balance the clock trees to make sure of setup and hold times.

On the other hand, an FPGA doesn't work this way. Clocks are routed on a clock network. Creating a clock mux of this sort adds a serious skew to the timing. As I am currently using Actel tools, I am very aware of the lack of any CTS done by the synthesis or place & route tools.

The key to solving these issues (other than manual regioning of individual flip-flops) is to carry the data over the delayed clock boundary by using buffers. BUFD is Actel's buffer primitive, and by using buffers on the data line you can eat from the setup-time and give to your hold-time. Basically take the output of ffA/ffB and add buffers between their Qs and the D of ffAB.

Voila, you have solved the hold time issue.

Another option is to create a copy of clkA and clkB, and delay those copies using a buffer. Then use these copies in your original clkA and clkB processes. This would create a smaller skew. The problem with this mechanism is that you may also want to sample data back from clkAB to clkA/clkB. If you play with the clock only, then it will be difficult to solve the hold problems in both directions. As soon as you solve it in one direction, you most likely will have created it in the other direction.

A good solution would use a combination of shifting the clock a bit, and using buffers to cover any remaining skew. Whatever solution you choose to implement should be done in direct conjunction with the timing analyzer tools. Those tools are your best way of knowing where and when you have setup or hold problems on your system.

Thursday, February 3, 2011

gvim 7.3 64-bit (x64) Windows 7, Vista, XP - w/ silent install & uninstall

This is an update to:
http://ionipti.blogspot.com/2010/08/gvim-73-64-bit-x64-windows-7-vista-xp.html

I have been asked over facebook to build gvim 7.3 with silent install and uninstall options. I have modified gvim.nsi to do that. The changes to gvim.nsi are below.

You can download the actual installer with silent support from: http://nkcorner.com/gvim73S.exe
Silent installs with all of the defaults.

---

MessageBox MB_YESNO|MB_ICONQUESTION \
"This will install Vim ${VER_MAJOR}.${VER_MINOR} on your computer.$\n Continue?" \
/SD IDYES IDNO DoAbort
Goto NoAbort
DoAbort:
Abort ; causes installer to quit.

---

MessageBox MB_YESNO|MB_ICONQUESTION \
"The installation process has been successful. Happy Vimming! \
$\n$\n Do you want to see the README file now?" /SD IDNO IDYES YesReadme
Goto NoReadme
YesReadme:
Exec '$0\gvim.exe -R "$0\README.txt"'

---

MessageBox MB_OK|MB_ICONINFORMATION \
"Vim ${VER_MAJOR}.${VER_MINOR} has been (partly) removed from your system" /SD IDOK
FunctionEnd

---

MessageBox MB_YESNO|MB_ICONQUESTION \
"Would you like to delete $0?$\n \
$\nIt contains the Vim executables and runtime files." /SD IDNO IDYES YesRemoveExes
Goto NoRemoveExes
YesRemoveExes:
Delete /REBOOTOK $0\*.dll

---

MessageBox MB_YESNO|MB_ICONQUESTION \
"Remove all files in your $1\vimfiles directory?$\n \
$\nCAREFUL: If you have created something there that you want to keep, click No" /SD IDNO IDYES NoFin
Goto Fin
NoFin:
RMDir /r $1\vimfiles

---

MessageBox MB_YESNO|MB_ICONQUESTION \
"Would you like to remove $0?$\n \
$\nIt contains your Vim configuration files!" /SD IDNO IDYES YesDelete
Goto NoDelete
YesDelete:
RMDir /r $0 ; skipped if no

---





Friday, December 10, 2010

dual clock fifo / ram - gray codes

A long time ago I tried to write a dual clock fifo, it was fun, and I somewhat succeeded. I have now done it again, only this time with a lot more knowledge and expertise.

The main issues are how to create a dual clock ram that is not dependent on the existence or speeds of the clocks involved, and how to keep track of full/empty/... signals in the different clock domains.

Creating a dual clock ram / dpr (dual port ram) is an interesting challenge. To do this properly, you can't just transfer requests from one domain to the other, b/c that's ugly... and the other clock might be off, it might be really slow in comparison, and bunches of other reasons. Perhaps you could create one really high speed clock, but that's ugly too, and costs more power, and is wasteful and more difficult to implement in my opinion.

So how do you do it? Use the wea (write enable port a), and web (write enable port b) as the inputs of an or gate which will be your clock to the ram elements. My code samples the write enables in their individual clock domains at the falling edge, and outputs a clean wea and web. Make sure the address decode logic is an input to the d of the wea and web, and not after. If the decode logic is after you will have lots of glitchy writes to addresses you didn't mean to write to. Use the we (wea or'ed with web) to clock the ram elements. Of course you will need a separate we signal for each ram element. So far this seems to work fairly reliably. You will need to check timing too...

Now onto the fifo full/empty signals. Obviously calculate the pointers against each other. But how do you get the pointer from the other side? Use a gray code!!! Convert the pointers to gray code in the originating clock domain (and sample them), then sample them twice in the destination clock domain. Then convert them back. That's the whole story. Pretty easy I'd say.

The reason you need a gray code is so that for a given increment of 1, the change in the gray code is always guaranteed to be only 1 bit of difference. The gray code I use is:
gray = (binary >> 1) ^ binary.

For example
000 = 000 >> 1 ^ 000 = 000 ^ 000 = 000
001 = 001 >> 1 ^ 001 = 000 ^ 001 = 001
010 = 010 >> 1 ^ 010 = 001 ^ 010 = 011
011 = 011 >> 1 ^ 011 = 001 ^ 011 = 010

Note that from binary 1 (001) to binary 2 (010) there are 2 bits that must change. If they are sampled without converting to gray code then you might sample the change from 1 to 2 as 011 (3). This is because the second bit arrived at the destination clock domain right before the rising clock and the first bit arrived right after the rising clock.
0 0 0 0 0 0 0 0 0      bit 2
0 0 0 0 0 0 1 1 1      bit 1
1 1 1 1 1 1 1 1 0      bit 0
......................|...      clock

This is an example of how you could clock in a completely incorrect value. Of course if the clock had risen a few moments later, or even a few moments earlier, you wouldn't have gotten this value, but when dealing with unrelated clock domains, you have way of guaranteeing when the clock will rise. Also note that this issue will come up regardless of how close you make the wires, b/c there will ALWAYS be a point in the middle where one of the bits updated and the other hasn't.

When using gray code you get:
1 = 001
2 = 011

0 0 0 0 0 0 0 0 0      bit 2
0 0 0 0 0 0 1 1 1      bit 1
1 1 1 1 1 1 1 1 1      bit 0
......................|...      clock

No matter where the clock might rise in the change between value 1 and value 2, you will either get the previous (value 1) or the new (value 2) value. You will never sample a completely incorrect value since only one bit changes.

For those like me looking for an easy bit of code to create a gray code:

Gray Code Functions in VHDL: (from memory)

function to_gray ( b : std_logic_vector(3 downto 0)) return std_logic_vector(3 downto 0)
 variable g : std_logic_vector(3 downto 0);
begin
 g := b xor ('0' & b(3 downto 1));
 return g;
end function;

function from_gray ( g : std_logic_vector(3 downto 0)) return std_logic_vector(3 downto 0)
 variable b : std_logic_vector(3 downto 0);
begin
 b(3) := g(3);
 for i in 0 to 2 loop
  b(2 - i) := b(3 - i) xor g(2 - i);
 end loop;
 return b;
end function;

Although a more efficient way to do this would be to build a table of some sort...

Sunday, September 12, 2010

Sadly Modelsim 6.6c has come out without fixing the bug I reported...

I reported a blatant bug in Modelsim's dealing with libraries. See: http://ionipti.blogspot.com/2010/07/modelsim-working-with-multiple.html.

And it's too bad that they haven't had the foresight to actually fix this problem.

What is nice is that they fixed a bunch of other bugs. There was one that was causing bad results and that had to do with queue slices when sent into tasks within other modules. That is now working correctly.

Saturday, August 28, 2010

Lattice / Xilinx FIFO resource issues

If you are like me, then when you design logic to interact with a FIFO, you make sure to carefully control your reads and writes. Since I do this for all FIFOs, it is very wasteful to have logic internal to the FIFO that checks whether I'm writing to a full FIFO, or reading from an empty FIFO.

Altera's tools allow you to choose whether or not you want such overflow and underflow logic in the FIFO. Xilinx and Lattice's tools do not. What this means is that every time you create and instantiate a FIFO with Lattice or Xilinx chips, there is logic that will negatively affect timing and use more resources, internal to the FIFO. This logic is so wasteful (and dangerous since it hides bugs), that I feel it important to point this out to the general public.

If you are using Lattice's IPExpress, the logic is shown in the Verilog or VHDL source files. This is great because it allows you to disable the logic with some simple comments.

For Xilinx, there doesn't appear to be a way to get at the source code that creates the logic, so I would strongly advise asking your FAE to request that Xilinx adds an option to Core Generator to allow for lighter weight FIFO creation that excludes this 'safety' feature.

In my system, I found a significant savings by hacking the (Lattice) created Verilog FIFO and disabling the empty and full check. Since my system depends on correct read and write logic, this was a function that was very useless to me. If I was reading when there was no data, or writing when there was no room - the system won't work anyhow - so I MAKE sure that doesn't happen in my logic.

Thursday, August 19, 2010

gvim 7.3 64-bit (x64) Windows 7, Vista, XP - build instructions and installer

This is an update to:
http://ionipti.blogspot.com/2010/04/build-gvim-72-x64-and-create-installer.html

You can download the actual installer from: http://nkcorner.com/gvim73.exe (64 bit only, includes all patches up to and including 7.3.003).

To create the 7.3 version, you must download:
ftp://ftp.vim.org/pub/vim/unix/vim-7.3.tar.bz2

There are no extra or lang archives. Everything is included in the main archive.

Download the 7.3 patches:
ftp://ftp.vim.org/pub/vim/patches/7.3/7.3.001
ftp://ftp.vim.org/pub/vim/patches/7.3/7.3.002
ftp://ftp.vim.org/pub/vim/patches/7.3/7.3.003

Follow all of the previous instructions (except for what relates to extra and lang archives).

Also modify the gvim.nsi file to change all instances of gvimext64.dll to gvimext.dll.

Other than that, it is all the same.

Monday, August 16, 2010

WRT54GL - GoogleWiFiSecure - OpenWRT

It had to be an eventuality that someone would try to get OpenWRT onto Google's secure wifi network, and that ended up being me. As I couldn't find any other information I had to figure this out for myself.

Here it is:
Use Backfire trunk (currently it's approximately 10.03.1 RC1). Use wpa_supplicant (instead of wpad or wpad-mini), and set it to use libopenssl. Add libopenssl. Add luci-medium too. Use the 47xx Broadcom as opposed to the old 2.4 kernel. Compile. Flash. Look for excellent compilation instructions on OpenWRT's website. I used Debian Lenny in a virtual machine (Oracle VirtualBox) and it went very smoothly.

After compiling and flashing, reboot and then telnet into the system and edit /lib/wifi/wpa_supplicant.sh line 84 from:
peap|ttls)
to
PEAP|TTLS)

It's a bug and hopefully they will fix it. The bug is either in LUCI or wpa_supplicant (probably depends who you ask), but this will get it working either way.

Now go into luci and set the settings. "Path-to-certificate" can be left blank. It will not affect anything (other than to verify server, but Google's WIFI doesn't support that). Set all the other settings as Google instructs:

To connect to GoogleWiFiSecure, create a new network connections profile with the following settings:

SSID: GoogleWiFiSecure (case-sensitive)

WPA/WPA2 Settings
EAP type: EAP-PEAP or EAP-TTLS
Encryption method: AES (preferred) or TKIP
Authentication Protocol: MS-CHAP-V2 or CHAP
Trusted Server Name for Authentication: onex.wifi.google.com

Under the 802.1x settings be sure to enter the username and password that you obtained earlier.

Reboot router and it should work.

Currently this is a NAT configuration. I'm sure with more work you can configure it to be a bridge, but as I passed the router back to my friend, and I have my own internet, I don't care anymore...

Here is a link to the compiled .trx file. You'll need the ability to burn the trx file.
http://nkcorner.com/openwrt-brcm47xx-squashfs.trx
Here is a link to the .config file I used:
http://nkcorner.com/openwrt-config (rename to .config)

PS - For the config, you just do make defconfig, and then choose the Broadcom BCM947xx/953xx (WITHOUT the 2.4!). If you use 2.4 then you won't have the 802.1x authentication which is required for Google's secure Wifi.
Then add wpa-supplicant (remove wpad-mini), and set wpa_supplicant to use libopenssl instead of internal. Then add libopenssl. Also add luci-medium if you want graphical configuration.
Everything else will work fine.

And YES - BCM947xx is the correct target, I know, I know, but the WRT54GL uses the BCM5352. Don't worry, it is the same chipset or something as the BCM947xx series...

This is it.

I'm sure that wpa_supplicant.sh can be changed when creating the image, but I never bothered with it, cause I don't care enough. If you make that change and would like to share how then by all means post a comment. Also note that it seems that after a change is made you will have to power cycle the router to get it to get back onto the wifi network. I don't know why, but I'm happy enough that it gets there once so I didn't bother trying to debug this. A mostly impossible step given my limited knowledge of how OpenWRT works anyhow.

As I don't have this router anymore, I'm probably not gonna update this blog much more unless someone posts an interesting comment which should be added.

Hopefully this will help someone.