Last April, I found this really amazing project on Github (made by xxuejie). The project fascinated me, but the website was a bit hard to use - I wanted people to know about his project and enjoy it like I had.
So I forked the project, added a stylesheet and huge mess of jQuery and deployed the newly modified version. (xxuejie loved it, but didn't have time to maintain it, so we kept the forked version)
Original version: http://qiezi.me/projects/mruby-web-irb/mruby.html
Modified version: http://joshnuss.github.io/mruby-web-irb/
A few months later the modified version landed on HackerNews. It was well received, got tons of visits and was tweeted all over the place. There were blog posts in Japanese & Chinese. Even folks at my supposed "arch enemy" Microsoft blogged about it.
Most people would call such an experience satisfying, but for some reason I wasn't sure :)
On one hand, it was a huge compliment to have a project so highly regarded by your peers, on the other hand it kind of miffed me...
How could a bunch of styling changes and user interface upgrades make such a difference? My other projects seemed more intricate & technical but were never received in that fashion.
So I chewed on it for a bit.
It turns out I made the classic techie mistake of thinking in purely technical terms. I had factored out the human element.
For us humans, vision is (arguably) our most important sense (highest bandwidth). When an object doesn't communicate a compelling visual element, it leaves an important channel to the brain unused and negatively impacts its usability potential.
Put another way, it doesn't matter how technically awesome something is - if it fails to register with a human brain, it will never be relevant. Some techies might consider that unfortunate, I don't (anymore), its just how the world works.
Without good design (and for that matter good content, marketing, information architecture & sales strategies) our software & hardware would live an inconsequential existence. Technology is just one piece of the puzzle.
Thursday, January 23, 2014
Thursday, September 20, 2012
FPGA Logic Cells
Lately I've seen lots of homebrew CPUs mentioned in blogs and videos, but not too much about homebrew FPGAs. Did a little digging (mostly on Wikipedia), and was able to breadboard a basic logic cell circuit. Here's how I did it.
![]() |
| A single logic cell. Using an Arduino to program the memory. Once the cell is programmed, the programming cable can be disconnected and the cell retains the truth table in memory. |
Background
A Field Programmable Gate Array is a matrix of reprogrammable logic blocks. Each logic block contains multiple logic cells.Each cell can be programmed to function as any type of logic gate! i.e. AND, OR, XOR etc..
This matrix of blocks are then woven together in a mesh that allows blocks to interconnect with other blocks, and of course these connections can be reprogrammed any time.
Today, I will focus on the logic cell.
Logic Cell
It performs a single boolean operation based on a pre-programmed truth table. Unlike when building gates from CMOS or TTL logic chips directly, The truth table is reprogrammable.For 2 input truth tables, there are 2^2=4 possible outcomes. Each of those outcomes is stored in a memory. We will use four D-Type Flip Flops (74HC174) to get the memory we need.
D-Type Flip Flop
The D-Type Flip Flop is a positive edge triggered flip flop. That means it will store the value of the data pin only when the clock pin goes high. After that, no matter what input it gets, the last value will be held, until it is cleared or a new value is clocked in. We can take 4 of these guys and tie the Clear and Clock pins together and voila - we have a 4 bit memory suitable for storing a truth table.Lookup Table
For example, consider an XOR gate, the four possible outcomes are (0,1,1,0)So now that we have that in our 4-bit memory, we need a way to select one of those four values depending on what the given 2 inputs are. For that we will use a 4-to-1 Multiplexer 74HC153 (also knows as decoder or selector). Or for any of you programming nerds out there, the hardware world's equivalent to a switch statement.
The 4-to-1 multiplexer has 4 possible outcomes labeled A-D and has 2 inputs called S0 and S1, the output is labeled Y. In pseudocode it would look something like this:
If S0=0 and S1=0 then Y=A
If S0=0 and S1=1 then Y=B
If S0=1 and S1=0 then Y=C
If S0=1 and S1=1 then Y=D
Logic cells can operate both in parallel or based on a clock. So another 1-bit memory is used to store whether the cell is clocked or not.
For now I will skip the clocking part, it is easy to add it. For now I will focus on the programmable lookup table.
Schematic

Breadboard
![]() |
| Example Breadboard Layout |
Programmer Firmware
An Arduino can be used to program the 4 bit memory over a serial connection. Here is an example sketch:#define A 2
#define B 3
#define C 4
#define D 5
#define CLOCK 10
#define CLEAR 9
#define HOLD 8
void truth_table(bool a, bool b, bool c, bool d) {
// print truth table
Serial.println(a ? "A=0,B=0,Y=1" : "A=0,B=0,Y=0");
Serial.println(b ? "A=1,B=0,Y=1" : "A=1,B=0,Y=0");
Serial.println(c ? "A=0,B=1,Y=1" : "A=0,B=1,Y=0");
Serial.println(d ? "A=1,B=1,Y=1" : "A=1,B=1,Y=0");
Serial.println("");
// clear flip flops
digitalWrite(CLEAR, LOW);
digitalWrite(CLEAR, HIGH);
// flip bits where neccessary
digitalWrite(A, a ? HIGH : LOW);
digitalWrite(B, b ? HIGH : LOW);
digitalWrite(C, c ? HIGH : LOW);
digitalWrite(D, d ? HIGH : LOW);
// toggle clock
digitalWrite(CLOCK, HIGH);
digitalWrite(CLOCK, LOW);
}
void setup() {
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(CLOCK, OUTPUT);
pinMode(CLEAR, OUTPUT);
pinMode(HOLD, OUTPUT);
// initialize clock pin
digitalWrite(CLOCK, LOW);
// initilize clear pin
digitalWrite(CLEAR, HIGH);
// initialize truth table
truth_table(false, false, false, false);
Serial.begin(9600);
}
char values[4] = {'F','F','F','F'};
char current;
int index = 0;
void loop() {
while (Serial.available()) {
current = Serial.read();
values[index] = current;
index++;
if (index == 4) {
truth_table(values[0] == 'T',
values[1] == 'T',
values[2] == 'T',
values[3] == 'T');
index = 0;
}
}
}
Using the Programmer
Open the Serial Monitor @ 9600, no line endingsTo send a truth table to the device, you will need to type 4 letters, either capital "T" or capital "F".
Examples:
For an AND gate type FFFT the LED will only light up when both buttons are pressed
For a NAND gate type TTTF the LED will light up except when both buttons are pressed
For an XOR gate type FTTF the LED will only light up when only one of the buttons is pressed
Now on to logic blocks and routing :)
Sunday, December 20, 2009
Find last updated date for ActiveRecord model
Often a page needs to show when content was last updated, its easy to do when showing a particular record, but what about the last updated date for a list of records?
This is a little snippet I use:
To make it happen for all models:
This is a little snippet I use:
date_of_last_fee_change = Fee.last_updated
To make it happen for all models:
ActiveRecord::Base.class_eval do
def self.last_updated
last = first(:order => 'updated_at DESC')
last.try(:updated_at)
end
end
Tuesday, August 11, 2009
Easily manage multiple apps
Sometimes you want to run a command on each of your rails apps. i.e. git pull or rake db:migrate
A simple ruby script can handle that:
each-app.rb:
This script iterates each folder under "/mnt/apps" directory, goes into that directory and executes each command.
you can run it like so:
just dont forget to
happy hacking
A simple ruby script can handle that:
each-app.rb:
#!/usr/bin/ruby
PATH = '/mnt/apps'
Dir.glob("#{PATH}/*").each do |dir|
Dir.chdir(dir)
puts "(in #{dir})"
ARGV.each { |cmd| system(cmd) }
end
This script iterates each folder under "/mnt/apps" directory, goes into that directory and executes each command.
you can run it like so:
./each-app.rb 'git pull' 'rake db:migrate'
just dont forget to
chmod +x each-app.rb
happy hacking
Friday, December 26, 2008
Why dont Array.first() and Array.last() accept blocks?
It would be nice if Array.first and Array.last took block parameters. That way you could get the first or last item in the array based on a block condition. Example:
As most things in ruby, its super easy to implement:
Heres how to implement this with the MRI interpreter. (Not as beautiful)
>> [1,2,3].first {|x| x > 1 }
=> 2
As most things in ruby, its super easy to implement:
class Array
def first
each do |x|
return x if !block_given? || yield(x)
end
end
def last
reverse_each do |x|
return x if !block_given? || yield(x)
end
end
end
a = [1,2,3]
puts a.first # => 1
puts a.first {|x| x>1 } # => 2
puts a.last # => 3
puts a.last {|x| x<2 } # => 1
Heres how to implement this with the MRI interpreter. (Not as beautiful)
static VALUE
rb_ary_first(argc, argv, ary)
int argc;
VALUE *argv;
VALUE ary;
{
if (argc == 0) {
if (rb_block_given_p()) {
long i;
RETURN_ENUMERATOR(ary, 0, 0);
for (i=0; ilen; i++) {
if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
return RARRAY(ary)->ptr[i];
}
}
return Qnil;
} else {
if (RARRAY(ary)->len == 0) return Qnil;
return RARRAY(ary)->ptr[0];
}
}
else {
return ary_shared_first(argc, argv, ary, Qfalse);
}
}
Quicksort
Array#sort works great, but its still fun to write a quick sort in ruby!
class Array
def quicksort
return self if length <= 1
pivot = shift
less, greater = partition {|x| x <= pivot}
less.quicksort + [pivot] + greater.quicksort
end
end
puts [8,99,4,1000,1,2,3,100,5,6].quicksort.join(',')
Wednesday, December 10, 2008
Paged Enumerable
Scraping some pages with WWW::Mechanize and finding myself needing to download multiple pages. So I wrote a paged enumerable module. Paged enumerable does everything Enumerable does, except instead of implementing a meaningful each you implement a meaningful each_page which yields an array or items per page.
to use the paged enumerable, you would write a class like this:
The implementation of PagedEnumerable is quite simple
and in most cases you would cache the pages for better performance on a second pass.
to use the paged enumerable, you would write a class like this:
class MultiplePageSearch
include PagedEnumerable
def each_page
10.times { |page| yield download_page(page) } # simulate 10 pages
end
private
def download_page(page)
puts "downloading page #{page}..."
sleep 1 # simulate slooow operation
start = page*10
start...(start+10)
end
end
paged = MultiplePageSearch.new
puts paged.any? {|x| x > 50} # will only hit 5 pages
paged.each {|x| puts x} # will process all pages
The implementation of PagedEnumerable is quite simple
module PagedEnumerable
def self.included(obj)
obj.send :include, Enumerable
end
def each(&blk)
each_page { |page| page.each(&blk) }
end
end
and in most cases you would cache the pages for better performance on a second pass.
Wednesday, December 3, 2008
Installing Git on CentOS 5.2 from sources
Installing Git SCM on Centos 5.2
Before running replace VERSION with the version you wish to install, usually the latest version will do fine.
Before running replace VERSION with the version you wish to install, usually the latest version will do fine.
yum install openssl-devel curl-devel expat-devel -y
wget http://www.kernel.org/pub/software/scm/git/git-VERSION.tar.gz
tar xvf git-VERSION.tar.gz
cd git-VERSION
make
make install
Subscribe to:
Posts (Atom)

