-
Welcome to Ternary Discord
04/01/2025 at 23:57 • 0 commentsI created Discord server dedicated to Balanced Ternary System hardware and software - this is temporary invite https://discord.gg/KJ3RQveW
Currently I'm working on VHDL implementation of TRIADOR to be runnable on virtually any FPGA board - previous logs about this topic:
- https://hackaday.io/project/28579/log/148618-simulating-ternary-in-vhdl
- https://hackaday.io/project/28579/log/148685-simulating-ternary-full-adder-in-vhdl
- https://hackaday.io/project/28579/log/148767-interfacing-ternary-and-binary-components
- https://hackaday.io/project/28579/log/148832-ternary-vhdl-package-v10-is-ready
- https://hackaday.io/project/28579/log/148920-ternary-counter-on-vhdl
P.S. Updated ternary.vhd to version 1.4
-
Ternary computer program emulator
04/04/2020 at 13:06 • 1 commentTo ease the debugging phase, here is an emulator for the ternary programs:
https://github.com/ssloy/triador
A very simple sample program that computes R2+R3 and stores the results back in R3:
R1 NNN # N.B. the memory is not guaranteed to be initialized RR NNN # initialize R13 with -13, this chooses segment NNN for jumps R2 ONP # write -2 to R2 ┌─────────────────────────────────┐ R3 PPP # write 13 to R3 │ This program computes R2+R3, │ RR OPO # copy R3 to R1 <─┐ │ the result is stored in R3. │ SK NPO # skip if R2!=0 │ │ Here is a C++ world equavalent: │ JP PNO # R2==0, terminate│ ────┐ │ │ SK PNP # skip if R2>0 │ │ │ int R2 = -2; │ RR OON # R1-- │ │ │ int R3 = 13; │ SK PNN # skip if R2<0 │ │ │ while (R2!=0) { │ RR OOP # R1++ │ │ │ int R1 = R3; │ RR ONO # copy R1 to R3 │ │ │ if (R2>0) R1++; │ RR OPN # copy R2 to R1 │ │ │ if (R2<0) R1--; │ SK PNP # skip if R2>0 │ │ │ R3 = R1; │ RR OOP # R1++ │ │ │ R1 = R2; │ SK PNN # skip if R2<0 │ │ │ if (R2>0) R1--; │ RR OON # R1-- │ │ │ if (R2<0) R1++; │ RR ONP # copy R1 to R2 │ │ │ R2 = R1; │ JP NOO # jump here ──────┘ │ │ } │ EX PPP # halt and catch fire <─┘ └─────────────────────────────────┘ -
Homebrew ternary mini-series (s1e5): random-access memory board
04/04/2020 at 13:04 • 0 commentsCheck for the subtitles if you do not speak russian.
-
Homebrew ternary mini-series (s1e4): memory cell
04/04/2020 at 13:03 • 0 comments -
Homebrew ternary mini-series (s1e3): multiplexers
04/04/2020 at 13:02 • 0 commentsCheck for the subtitles if you do not speak russian.
-
Homebrew ternary mini-series (s1e2): balanced ternary basics
03/31/2020 at 09:10 • 0 commentsCheck for the subtitles if you do not speak russian.
-
Homebrew ternary mini-series (s1e1): IO card
03/31/2020 at 09:09 • 0 commentsCheck for the subtitles if you do not speak russian.
-
TRIADOR ALU
07/10/2018 at 19:39 • 0 commentsBack to the true ternary triador implementation. The ALU will look like this:
![]()
Compare it to the overall schematics of the triador in the project details. All input signals are shown on the left of the boxes, all output signals are on the right. The ALU consists of 5 slices (one per operation) plus one trit memory cell for the borrow/carry flag. This flag won't be accessible outside of the ALU, its value is set by the RR slice, and is read by the SK slice only.
Just to give you an idea of what the boxes look inside, here is the OP box (tritwise unary operation over R1 register):
![]()
-
Ternary counter on VHDL
07/08/2018 at 09:26 • 2 commentsI just literally copied this ternary counter design from 2011 into VHDL:
![]()
This is VHDL source code (only 3 trits were used):
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use ternary.all; entity main is Port ( clk : in bit; res : in bit; a : in FakeTrit; b : in FakeTrit; c : in FakeTrit; s1 : out FakeTrit; s2 : out FakeTrit; s3 : out FakeTrit; led : out bit); end main; architecture Behavioral of main is signal a1,b1,c1,ss0,ss1,ss2,ss3,ss4 : FakeTrit; signal pclk,pclk1,nclk,nclk1,res0,res1,m1,m2,m3,m4,h1,h2,h3,h4,g1,g2,g3 : FakeTrit; signal tmp_clk_std, tmp_iclk_std : STD_LOGIC; signal tmp_clk : BIT; COMPONENT Clock_Divider PORT( clk : IN std_logic; reset : IN std_logic; clock_out : OUT std_logic ); END COMPONENT; COMPONENT Half_Adder Port ( a : in FakeTrit; b : in FakeTrit; s : out FakeTrit; c : out FakeTrit ); END COMPONENT; FUNCTION io_invert(T: FakeTrit) RETURN FakeTrit IS begin case T is when N => return P; when O => return X; when P => return N; when others => return O; end case; end; begin a1 <= io_invert(a); b1 <= io_invert(b); c1 <= io_invert(c); tmp_iclk_std <= to_stdulogic(clk); div1: Clock_Divider port map( clk => tmp_iclk_std, reset => '0', clock_out => tmp_clk_std ); tmp_clk <= to_bit(tmp_clk_std); res0(0) <= '0'; res0(1) <= res; res1 <= res0; clk1: ternary_clock port map( B_C => tmp_clk, T_C => ss0 ); mux1: ternary_mux port map( T_S => ss0, T_N => O, T_O => O, T_P => P, T_C => pclk ); mux2: ternary_mux port map( T_S => res1, T_N => pclk, T_O => N, T_P => N, T_C => pclk1 ); mux3: ternary_mux port map( T_S => ss0, T_N => N, T_O => O, T_P => O, T_C => nclk ); mux4: ternary_mux port map( T_S => res1, T_N => nclk, T_O => P, T_P => P, T_C => nclk1 ); mem1: ternary_mem port map( T_S => pclk1, T_N => O, T_P => ss1, T_Q => m1 ); ha1: Half_Adder port map( a => P, b => m1, s => h1, c => g1 ); mem2: ternary_mem port map( T_S => nclk1, T_N => h1, T_P => O, T_Q => ss1 ); mem3: ternary_mem port map( T_S => pclk1, T_N => O, T_P => ss2, T_Q => m2 ); ha2: Half_Adder port map( a => g1, b => m2, s => h2, c => g2 ); mem4: ternary_mem port map( T_S => nclk1, T_N => h2, T_P => O, T_Q => ss2 ); mem5: ternary_mem port map( T_S => pclk1, T_N => O, T_P => ss3, T_Q => m3 ); ha3: Half_Adder port map( a => g2, b => m3, s => h3, c => g3 ); mem6: ternary_mem port map( T_S => nclk1, T_N => h3, T_P => O, T_Q => ss3 ); s1 <= io_invert(ss1); s2 <= io_invert(ss2); s3 <= io_invert(ss3); led <= to_bit(tmp_clk_std); end Behavioral;Every trit in the counter takes 2 macrocells on CoolRunner-II, so technically CoolRunner-II chip XC2C256 with 256 macrocells may have up to 128-trit counter inside! This is pin assignments for XC2-XL board (a,b,c are not used here):
NET "clk" LOC = "P38" | IOSTANDARD = LVTTL ; NET "res" LOC = "P143" | IOSTANDARD = LVTTL ; NET "a<0>" LOC = "P140" | IOSTANDARD = LVTTL ; NET "a<1>" LOC = "P142" | IOSTANDARD = LVTTL ; NET "b<0>" LOC = "P138" | IOSTANDARD = LVTTL ; NET "b<1>" LOC = "P139" | IOSTANDARD = LVTTL ; NET "c<0>" LOC = "P136" | IOSTANDARD = LVTTL ; NET "c<1>" LOC = "P137" | IOSTANDARD = LVTTL ; NET "s3<0>" LOC = "P82" | IOSTANDARD = LVTTL ; NET "s3<1>" LOC = "P83" | IOSTANDARD = LVTTL ; NET "s2<0>" LOC = "P85" | IOSTANDARD = LVTTL ; NET "s2<1>" LOC = "P86" | IOSTANDARD = LVTTL ; NET "s1<0>" LOC = "P87" | IOSTANDARD = LVTTL ; NET "s1<1>" LOC = "P88" | IOSTANDARD = LVTTL ; NET "led" LOC = "P92" | IOSTANDARD = LVTTL ;
and this is a video that proves that it's working on Xilinx CoolRunner-II ;)
-
Ternary VHDL package v1.0 is ready
07/06/2018 at 09:30 • 0 commentsSo now we have everything to build a ternary computer in binary CPLD (or FPGA)::
https://cdn.hackaday.io/files/285791222723936/ternary.vhd
Last pieces were just added:
- procedure dmux:
PROCEDURE dmux(signal T_S: IN FakeTrit; signal T_C: IN FakeTrit; signal T_N: OUT FakeTrit; signal T_O: OUT FakeTrit; signal T_P: OUT FakeTrit) IS begin case T_S is when N => T_N <= T_C; T_O <= O; T_P <= O; when O => T_N <= O; T_O <= T_C; T_P <= O; when P => T_N <= O; T_O <= O; T_P <= T_C; when others => T_N <= X; T_O <= X; T_P <= X; end case; end;- component ternary_mem:
-- Entity ternary_mem USE ternary.ALL; ENTITY ternary_mem IS PORT ( T_S : IN FakeTrit; T_N : IN FakeTrit; T_P : IN FakeTrit; T_Q : OUT FakeTrit ); END ternary_mem; ARCHITECTURE Behavioral OF ternary_mem IS BEGIN PROCESS (T_S) BEGIN IF (T_S(0) = '1') THEN T_Q <= T_P; ELSIF (T_S(1) = '1') THEN T_Q <= T_N; END IF; END PROCESS; END Behavioral;- component ternary_clock:-- Entity ternary_clock USE ternary.ALL; ENTITY ternary_clock IS PORT ( B_C : IN BIT; T_C : OUT FakeTrit ); END ternary_clock; ARCHITECTURE Behavioral OF ternary_clock IS signal flag : bit; BEGIN PROCESS (B_C) BEGIN IF (B_C'event AND B_C = '1') THEN IF (flag = '0') THEN flag <= '1'; ELSE flag <= '0'; END IF; END IF; IF (B_C = '1' AND flag = '0') THEN T_C(0) <= '1'; ELSE T_C(0) <= '0'; END IF; IF (B_C = '1' AND flag = '1') THEN T_C(1) <= '1'; ELSE T_C(1) <= '0'; END IF; END PROCESS; END Behavioral;Last one turns binary clock into ternary sequence NOPONOPONOPO...
Also I renamed some variable names and added additional components for mux and dmux ( just in case if somebody doesn't like them as functions ; )
Dmitry V. Sokolov

