Close
0%
0%

Homebrew ternary computer

TRIADOR: The only ternary computer made for real in the past 50 years.

Public Chat
Similar projects worth following
Many claimed to build a ternary computer, however nobody (to the best of my knowledge) completed the project. TRIADOR project makes no empty promises!

In this project we are trying to build a very simple but functional 3-trit balanced ternary computer. The only building block allowed is a ternary multiplexer based on DG403 analog switches. 

 ══════════════════════════════════════════════════════════
        Description of the ternary computer TRIADOR
 ══════════════════════════════════════════════════════════
  General purpose
     registers
 trits 2 1 0  (a trit can take -1,0,+1 values)
      ┌─┬─┬─┐
    R1│ │ │ │ \
      ├─┼─┼─┤ |                              Program memory
    R2│ │ │ │ |                            trits 4 3 2 1 0
      ├─┼─┼─┤ |-- main set                      ┌─┬─┬─┬─┬─┐
    R3│ │ │ │ |   of registers           a  -364│ │ │ │ │ │
      ├─┼─┼─┤ |                          d      ├─┼─┼─┼─┼─┤
    R4│ │ │ │ /                          d  -363│ │ │ │ │ │
      ├─┼─┼─┤                            r      ├─┼─┼─┼─┼─┤
    R5│ │ │ │ \                          e  -362│ │ │ │ │ │
      ├─┼─┼─┤ |                          s      ├─┼─┼─┼─┼─┤
    R6│ │ │ │ |                          s      : : : : : :
      ├─┼─┼─┤ |                                 ├─┼─┼─┼─┼─┤
    R7│ │ │ │ |                              -1 │ │ │ │ │ │
      ├─┼─┼─┤ |                                 ├─┼─┼─┼─┼─┤
    R8│ │ │ │ |                               0 │ │ │ │ │ │
      ├─┼─┼─┤ |-- extra registers               ├─┼─┼─┼─┼─┤
    R9│ │ │ │ |                              +1 │ │ │ │ │ │
      ├─┼─┼─┤ |                                 ├─┼─┼─┼─┼─┤
   R10│ │ │ │ |                                 : : : : : :
      ├─┼─┼─┤ |                                 ├─┼─┼─┼─┼─┤
   R11│ │ │ │ |                             +363│ │ │ │ │ │
      ├─┼─┼─┤ |                                 ├─┼─┼─┼─┼─┤
   R12│ │ │ │ /                             +364│ │ │ │ │ │
      ├─┼─┼─┤                                   └─┴─┴─┴─┴─┘
   R13│ │ │ │ Special register,
      └─┴─┴─┘ specifies memory segment for JP ttt
      ┌─┬─┬─┬─┬─┬─┐
   PC │ │ │ │ │ │ │ program counter register (-364..+364)
      └─┴─┴─┴─┴─┴─┘
      ┌─┐
    C │ │ borrow/carry flag (+1 borrow, -1 carry)
      └─┘

  Every 3-trit register can take values from -13 to +13:

  t2*9 + t1*3 + t0

  where t0,t1,t2 - trits (-1,0,+1)

 ═══════════════════════════════════════════════════════════
                 TRIADOR instruction set
 ═══════════════════════════════════════════════════════════
  (for lisibility we use N,O,P instead of -1,0,+1)
  ┌───────┬────────┬───────────────────────────────────────┐
  │op code│mnemonic│ description                           │
  ├───────┼────────┼───────────────────────────────────────┤
  │ NNttt │ EX ttt │ extension commands (work in progress) │
  ├───────┼────────┼───────────────────────────────────────┤
  │ NOttt │ JP ttt │ unconditional jump to R13*27+ttt      │
  ├───────┼────────┼───────────────────────────────────────┤
  │ NPttt │ SK ttt │ conditional skips of the next command │
  ├───────┼────────┼───────────────────────────────────────┤
  │ ONttt │ OP ttt │ tritwise unary operation over R1      │
  ├───────┼────────┼───────────────────────────────────────┤
  │ OOttt │ RR ttt │ copying between registers             │
  ├───────┼────────┼───────────────────────────────────────┤
  │ OPttt │ R1 ttt │ write ttt to the register R1          │
  ├───────┼────────┼───────────────────────────────────────┤
  │ PNttt │ R2 ttt │ write ttt to the register R2          │
  ├───────┼────────┼───────────────────────────────────────┤
  │ POttt │ R3 ttt │ write ttt to the register R3          │
  ├───────┼────────┼───────────────────────────────────────┤
  │ PPttt │ R4 ttt │ write ttt to the register R4          │
  └───────┴────────┴───────────────────────────────────────┘

  Additional comments:
  - ttt means a 3-trit number with values
    from NNN (-13) to PPP (+13)

  - RR ttt instruction copies a register to/from R1 or performs
    an increment/decrement over R1
    OONNN — copy R1 to R13
    OONNO — copy R1 to R12
    OONNP — copy R1 to R11
    OONON — copy R1 to R10
    OONOO — copy R1 to R9
 OONOP...
Read more »

TRIADOR.TXT

Specification of ternary computer TRIADOR

plain - 7.74 kB - 04/02/2025 at 01:25

Download

ternary.vhd

Ternary VHDL package v1.3

x-vhdl - 9.03 kB - 04/02/2025 at 01:23

Download

ternary_circ.zip

Archive with TRIADOR simulation for Logisim v2.7.1 and also couple TXT files with some details from Shaos

Zip Archive - 49.99 kB - 06/23/2018 at 04:32

Download

  • 600 × dg403dy Switches and Multiplexers / Analog Switches and Multiplexers

  • Welcome to Ternary Discord

    SHAOS11 hours ago 0 comments

    I 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:

    Updated ternary.vhd to version 1.3

  • Ternary computer program emulator

    Dmitry V. Sokolov04/04/2020 at 13:06 1 comment

    To 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

    Dmitry V. Sokolov04/04/2020 at 13:04 0 comments

    Check for the subtitles if you do not speak russian.

  • Homebrew ternary mini-series (s1e4): memory cell

    Dmitry V. Sokolov04/04/2020 at 13:03 0 comments

  • Homebrew ternary mini-series (s1e3): multiplexers

    Dmitry V. Sokolov04/04/2020 at 13:02 0 comments

    Check for the subtitles if you do not speak russian.

  • Homebrew ternary mini-series (s1e2): balanced ternary basics

    Dmitry V. Sokolov03/31/2020 at 09:10 0 comments

    Check for the subtitles if you do not speak russian.

  • Homebrew ternary mini-series (s1e1): IO card

    Dmitry V. Sokolov03/31/2020 at 09:09 0 comments

    Check for the subtitles if you do not speak russian.

  • TRIADOR ALU

    Dmitry V. Sokolov07/10/2018 at 19:39 0 comments

    Back 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

    SHAOS07/08/2018 at 09:26 2 comments

    I 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

    SHAOS07/06/2018 at 09:30 0 comments

    So 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 ; )

View all 25 project logs

Enjoy this project?

Share

Discussions

roelh wrote 2 hours ago point

Hi, I designed a flip-flap-flop (flipflop with 3 states) with only 2 transistors, described in the latest log of this project: https://hackaday.io/project/162405-2-transistor-4-state-flipflop

  Are you sure? yes | no

Farooq Karimi Zadeh wrote 03/06/2021 at 09:02 point

Hello. Your project seems interesting but dg403 is so expensive and is not easy to find.

I don't know much about Electronics but perhaps they can be replaced with 4016? 300 of them have enough analogue switches and the price will be reduced a LOT.

  Are you sure? yes | no

SHAOS wrote 05/30/2023 at 03:38 point

No. 4016 uses the same power for logic and analog parts, but DG403 uses different power pins for logic and analog so we can play with it shifting thresholds to achieve desirable results: http://shaos.net/blog/2010/08/ternary-multiplexer-selector.html

  Are you sure? yes | no

alice crush wrote 10/06/2020 at 07:41 point

I am very impressed by your project. I espescialy liked your io board.
I setout to write multiply, I finished but I am doubtfull that it
would work.  I really wanted call/return, and unconditional skip.
please put them in your cpu.  I am also suprised to find no a in your
alu.  Atleast put in add.  And another glaring whole is the inability
to write to memory.  Just a simple load and store... though a stack
wants to index memory by a register.  So my wishlist is call/return
add and write to memory. in detail...

add  r1,rn      ; r1= r1+rn
skip            ; always
call ttt        ; the usual, like jp, saving the return address on
                ; the stack
return          ; pc= top of stack, drop the return address from the stack
RM ttt,rn       ; memory[r13*27+ttt]= rn
RIdxM ttt,r1,rn ; memory[r13*27+ttt+r1]= rn

simple but addequit. I would implment them in the order listed.  Do
you have any thoughts?  Please let me know.  Oh a halt instruction and
a way to ring a bell would be nice!  But adding to the list of features
is always easy.  :)  Whats hard is leaving them out!

what folllows is my first atempt at a muliply..... and a joke...

-360: oppoo R1 POO / r1=9
-359: oppnn R2 PNN ; r2=5
-358: noooo jp ooo ; trimul
----: ;;; result in r5,6= 45
-357: oppon R1 PON ; r1=8
-356: oppn0 R2 PNO ; r2=6
-355: noooo jp ooo ; trimul
====: ;;; result in r5,6= 42 more or less
-354: nottt jp halt ; jp .

----: ; trimul
000: ooopp RR r4,r1
001: oooon dec r1
002: npopp skg r1
003: xxxxx ret
004: nopoo jp triadd
005: nooop jp oop ; again...
006: ; end

----: ; triadd result r5,6
009: ooonp RR r1,r2
010: oooon dec r1
011: npopp skg r1
012: nopnn jp pnn;ret
013: oopop RR r5,r1
014: oopno RR r1,r6
014: oooop inc r1
015: oopoo RR r6,r1
016: npoon skc -1
     npppp skip r4>0
017: nopop JP pop
018: oopnn RR r1,r5
019: oooop inc r1
020: oopop RR r5,r1
021: nopop JP pop

  Are you sure? yes | no

alice crush wrote 09/19/2020 at 23:21 point

your alu doesnt do addition?

  Are you sure? yes | no

xor wrote 08/01/2018 at 11:29 point

better way is 0 1 *

please read book evolution algorithm goldberd

  Are you sure? yes | no

SHAOS wrote 08/02/2018 at 00:58 point

no it is not :)

  Are you sure? yes | no

fabian wrote 08/04/2018 at 13:13 point

this is not trouble caligraphy ,

read abou genetic algorithm

  Are you sure? yes | no

SHAOS wrote 05/30/2023 at 03:39 point

I used genetic algorithms in 90s...

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates