diff --git a/.gitignore b/.gitignore index 0be0f35..aaae97c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -*/wave -*/work +**/wave +**/work diff --git a/scripts/build_env.py b/scripts/build_env.py index b6fceff..d9ff493 100644 --- a/scripts/build_env.py +++ b/scripts/build_env.py @@ -43,20 +43,16 @@ def addAllVHDLFiles(std: str, lib: str, init=False): return -1 vhdlFiles = [] currentlyAdded = [] - absWorkDir = os.path.join(os.getcwd(), "work") cfFileId = getCfFileId(std) ## Find already present files if not init: - cfFileName = list(filter(lambda x: ".cf" in x and lib in x and cfFileId in x, os.listdir(absWorkDir)))[0] - cfFilePath = os.path.join(absWorkDir,cfFileName) + cfFileName = list(filter(lambda x: ".cf" in x and lib in x and cfFileId in x, os.listdir("work")))[0] + cfFilePath = os.path.join("work",cfFileName) currentlyAdded = getCurrentlyAddedFiles(cfFilePath) - print(currentlyAdded) ## Add files not added for file in os.listdir(): - print(file) - print(currentlyAdded, file, file not in currentlyAdded) if ".vhd" in file and file not in currentlyAdded: - vhdlFiles.append(os.path.join(os.getcwd(), file)) + vhdlFiles.append(file) if len(vhdlFiles) > 0: print(f"Detected new files. Adding {vhdlFiles}") command = ["ghdl", "-i", "--workdir=work", f"--work={lib}", f"--std={std}"] + vhdlFiles @@ -68,5 +64,5 @@ def getCurrentlyAddedFiles(cfFilePath:str): lines = f.readlines() f.close() fileLines = filter(lambda x: "file" in x, lines) - files = map(lambda x: split("\"",x)[1], fileLines) + files = map(lambda x: split("\" \"",x)[1], fileLines) return list(files) diff --git a/scripts/elab.py b/scripts/elab.py index 1d5a040..7cf0edc 100644 --- a/scripts/elab.py +++ b/scripts/elab.py @@ -7,7 +7,7 @@ from typing import List def generateIncludesForGHDL(includes: List[str]): cmd = [] for inc in includes: - cmd.append(f"-P{os.path.join(os.getcwd(), f"{inc}/work")}") + cmd.append(f"-P{inc}/work") return cmd def elabDesign(topDef: str, arch: str, lib: str, std: str, includes: List[str]): @@ -26,7 +26,7 @@ def runDesign(topDef: str, arch: str, lib: str, std: str, includes): print("Elaboration failed...") return -1 os.makedirs("wave",exist_ok=True) - wavePath = os.path.join(os.getcwd(), "wave") + wavePath = "wave" incs = generateIncludesForGHDL(includes) command = [ ## may add -v for verbose "ghdl", "--elab-run", f"--workdir=work", f"--work={lib}", f"--std={std}"] + incs + ["-o", f"work/{topDef}-{arch}", f"{topDef}", f"{arch}", diff --git a/src/control_socbridge_merge/control_socbridge_tb.vhd b/src/control_socbridge_merge/control_socbridge_tb.vhd new file mode 100644 index 0000000..dc15769 --- /dev/null +++ b/src/control_socbridge_merge/control_socbridge_tb.vhd @@ -0,0 +1,218 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.NUMERIC_STD.all; +library ganimede; +use ganimede.io_types.all; +library socbridge; +use socbridge.socbridge_driver_tb_pkg.all; +library controller; + +entity control_socbridge_tb is +end entity control_socbridge_tb; + +architecture tb of control_socbridge_tb is + + constant CLK_PERIOD : Time := 10 ns; + constant SIMULATION_CYCLE_COUNT : integer := 2000; + signal clk, rst : std_logic := '0'; + signal controller_to_socbridge_driver_cmd : command_t; + signal controller_to_socbridge_driver_address : std_logic_vector(31 downto 0); + signal cmd_size : positive; + signal ext_to_socbridge_driver : ext_to_socbridge_driver_t := ( + payload => (others => '0'), + control => (others => '0') + ); + signal socbridge_driver_to_ext : socbridge_driver_to_ext_t; + signal socbridge_driver_to_buffer : socbridge_driver_to_buffer_t; + signal buffer_to_socbridge_driver : buffer_to_socbridge_driver_t := ( + payload => (others => '0'), + write_enable_out => '0', + is_full_in => '0' + ); + signal cpu_to_controller: cpu_to_controller_t := ( + driver_id => (others => '0'), + address => (others => '0'), + seq_mem_access_count => 0, + cmd => "00" + ); + signal socbridge_driver_to_controller: socbridge_driver_to_controller_t := (is_active => '0'); + signal controller_to_cpu: controller_to_cpu_t; + signal controller_to_socbridge_driver: controller_to_socbridge_driver_t; + + signal curr_word : std_logic_vector(ext_to_socbridge_driver.payload'length - 1 downto 0); + signal expected_out : std_logic_vector(socbridge_driver_to_ext.payload'length - 1 downto 0); + + procedure fail(error_msg : string) is + begin + wait for CLK_PERIOD; + report "Simulation ending due to: " & error_msg & ". Shutting down..." severity FAILURE; + end procedure; + + procedure check_next_state(correct_state: state_t) is + begin + if(not (correct_state = G_next_state)) then + report "Next State is not what was expected, found " & state_t'image(G_next_state) + & " but expected " & state_t'image(correct_state) severity error; + fail("Next State"); + end if; + end procedure; + + procedure check_data_out(correct_data: std_logic_vector(socbridge_driver_to_ext.payload'length - 1 downto 0)) is + begin + if(not (correct_data = socbridge_driver_to_ext.payload)) then + report "Data out is not what was expected, found " & to_string(socbridge_driver_to_ext.payload) + & " but expected " & to_string(correct_data) severity error; + fail("Data out"); + end if; + end procedure; + + procedure check_parity(correct_data: std_logic_vector(socbridge_driver_to_ext.payload'length - 1 downto 0)) is + begin + if(not (calc_parity(correct_data) = calc_parity(socbridge_driver_to_ext.payload))) then + report "Parity out is not what was expected, found " & std_logic'image(calc_parity(socbridge_driver_to_ext.payload)) + & " but expected " & std_logic'image(calc_parity(correct_data)) severity error; + fail("Parity out"); + end if; + end procedure; + +begin + socbridge_inst: entity socbridge.socbridge_driver + port map( + clk => clk, + rst => rst, + controller_to_socbridge_driver => controller_to_socbridge_driver, + socbridge_driver_to_controller => socbridge_driver_to_controller, + ext_to_socbridge_driver => ext_to_socbridge_driver, + socbridge_driver_to_ext => socbridge_driver_to_ext, + buffer_to_socbridge_driver => buffer_to_socbridge_driver, + socbridge_driver_to_buffer => socbridge_driver_to_buffer + ); + + controller_unit_inst: entity controller.control_unit + port map( + clk => clk, + rst => rst, + cpu_to_controller => cpu_to_controller, + controller_to_cpu => controller_to_cpu, + socbridge_driver_to_controller => socbridge_driver_to_controller, + controller_to_socbridge_driver => controller_to_socbridge_driver + ); + + ext_to_socbridge_driver.control(1) <= clk; + controller_clock_proc: process + begin + for i in 0 to SIMULATION_CYCLE_COUNT - 1 loop + wait for CLK_PERIOD / 2; + clk <= not clk; + end loop; + wait; + end process controller_clock_proc; + + stimulus_proc: process + begin + report "Starting Simulation Stimulus!"; + rst <= '1'; + cpu_to_controller.address <= (others => '0'); + cpu_to_controller.cmd <= "00"; + cpu_to_controller.driver_id <= "1"; + cpu_to_controller.seq_mem_access_count <= 256; + wait for 3 * CLK_PERIOD; + report "Reset grace period ended, starting stimulus..."; + rst <= '0'; + cpu_to_controller.address <= x"FA0FA0FA"; + cpu_to_controller.cmd <= "01"; + wait until socbridge_driver_to_controller.is_active = '1'; + report "Task received in driver, awaiting completion..."; + cpu_to_controller.address <= (others => '0'); + cpu_to_controller.cmd <= "00"; + wait until socbridge_driver_to_controller.is_active = '0'; + wait for CLK_PERIOD; + report "Task completed in driver, sending next task..."; + cpu_to_controller.address <= x"FA0FA0FA"; + cpu_to_controller.cmd <= "10"; + wait for CLK_PERIOD; + wait until socbridge_driver_to_controller.is_active = '1'; + report "Task received in driver, awaiting completion..."; + cpu_to_controller.address <= (others => '0'); + cpu_to_controller.cmd <= "00"; + wait until socbridge_driver_to_controller.is_active = '0'; + wait for CLK_PERIOD; + report "Task completed in driver, ending simulation stimulus"; + cpu_to_controller.address <= (others => '0'); + cpu_to_controller.cmd <= "00"; + cpu_to_controller.driver_id <= "0"; + cpu_to_controller.seq_mem_access_count <= 0; + + wait; + end process stimulus_proc; + + external_stimulus_signal: process(curr_word) + begin + ext_to_socbridge_driver.payload <= curr_word; + ext_to_socbridge_driver.control(0) <= calc_parity(curr_word); + end process external_stimulus_signal; + + external_stimulus: process + variable input : positive := 1; + begin + wait for CLK_PERIOD / 1000; + curr_word <= "00000000"; + wait for 999 * CLK_PERIOD / 1000; + wait for 2 * CLK_PERIOD; + wait for CLK_PERIOD / 2; + wait for 10* CLK_PERIOD; + curr_word <= "00001001"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + wait for CLK_PERIOD * 140; + curr_word <= "00101001"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + wait for CLK_PERIOD * 140; + curr_word <= "00101001"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + wait for CLK_PERIOD * 20; + curr_word <= "01100111"; + wait for CLK_PERIOD; + for x in 0 to 127 loop + curr_word <= std_logic_vector(to_unsigned(input, 8)); + input := input + 1 mod 256; + wait for CLK_PERIOD; + end loop; + curr_word <= "00000000"; + wait for CLK_PERIOD * 140; + wait for CLK_PERIOD * 20; + curr_word <= "01100111"; + wait for CLK_PERIOD; + for x in 0 to 127 loop + curr_word <= std_logic_vector(to_unsigned(input, 8)); + input := input + 1 mod 256; + wait for CLK_PERIOD; + end loop; + + wait; + end process external_stimulus; + + internal_stimulus: process + variable input : positive := 1; + begin + buffer_to_socbridge_driver.is_full_in <= '0'; + buffer_to_socbridge_driver.write_enable_out <= '0'; + wait for 3 * CLK_PERIOD; + -- stimulus goes here + buffer_to_socbridge_driver.write_enable_out <= '1'; + buffer_to_socbridge_driver.payload <= std_logic_vector(to_unsigned(input, buffer_to_socbridge_driver.payload'length)); + input := input + 1 mod 256; + wait until rising_edge(clk) and socbridge_driver_to_buffer.is_full_out = '0'; + wait until falling_edge(clk); + for x in 0 to 1000 loop + buffer_to_socbridge_driver.payload <= std_logic_vector(to_unsigned(input, buffer_to_socbridge_driver.payload'length)); + input := input + 1 mod 256; + wait until rising_edge(clk) and socbridge_driver_to_buffer.is_full_out = '0'; + wait until falling_edge(clk); + end loop; + wait; + end process internal_stimulus; + +end architecture tb; diff --git a/src/controller/control_unit.vhd b/src/controller/control_unit.vhd new file mode 100644 index 0000000..66f4870 --- /dev/null +++ b/src/controller/control_unit.vhd @@ -0,0 +1,77 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.MATH_REAL.all; +library ganimede; +use ganimede.io_types.all; + +entity control_unit is + + port ( + clk, rst : in std_logic; + cpu_to_controller : in cpu_to_controller_t; + controller_to_cpu : out controller_to_cpu_t; + socbridge_driver_to_controller : in socbridge_driver_to_controller_t; + controller_to_socbridge_driver : out controller_to_socbridge_driver_t + ); + +end entity control_unit; + +architecture behave of control_unit is + type state_t is record + address: std_logic_vector(address_width - 1 downto 0); + seq_mem_access_count: integer; + curr_driver: std_logic; + ready: std_logic; + instruction: instruction_command_t; + end record state_t; + + signal state: state_t; + shared variable ored: std_logic; + + +begin + + comb_proc: process(cpu_to_controller, socbridge_driver_to_controller, state) + begin + ored := '0'; + ready_reduction: for i in 0 to number_of_drivers - 1 loop + ored := ored or socbridge_driver_to_controller.is_active; + end loop ready_reduction; + controller_to_socbridge_driver.request <= state.curr_driver; + controller_to_socbridge_driver.address <= state.address; + controller_to_socbridge_driver.seq_mem_access_count <= state.seq_mem_access_count; + controller_to_cpu.ready <= state.ready; + controller_to_socbridge_driver.instruction <= state.instruction; + end process comb_proc; + + sync_proc: process(clk, state) + begin + if rising_edge(clk) then + if rst = '1' then + state <= ((others => '0'), + 0, + '0', + '1', + NO_OP); + else + state.ready <= not ored; + if ored = '0' then + state.address <= cpu_to_controller.address; + state.seq_mem_access_count <= cpu_to_controller.seq_mem_access_count; + state.curr_driver <= cpu_to_controller.driver_id(0); + with cpu_to_controller.cmd select + state.instruction <= WRITE when "01", + READ when "10", + NO_OP when others; + else + state <= ((others => '0'), + 0, + '0', + '1', + NO_OP); + end if; + end if; + end if; + end process sync_proc; + +end architecture behave; diff --git a/src/controller/control_unit_tb.vhd b/src/controller/control_unit_tb.vhd new file mode 100644 index 0000000..905166d --- /dev/null +++ b/src/controller/control_unit_tb.vhd @@ -0,0 +1,90 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.MATH_REAL.all; +use IEEE.numeric_std.all; +library ganimede; +use ganimede.io_types.all; +library controller; + +entity control_unit_tb is +end entity control_unit_tb; + +architecture tb of control_unit_tb is + + constant cycle: Time := 10 ns; + signal clock: std_logic := '0'; + signal reset: std_logic := '0'; + signal cpu_to_controller: cpu_to_controller_t := ( + (others => '0'), + (others => '0'), + 0, + "00"); + signal socbridge_driver_to_controller: socbridge_driver_to_controller_t := (is_active => '0'); + signal controller_to_cpu: controller_to_cpu_t; + signal controller_to_socbridge_driver: controller_to_socbridge_driver_t; + signal current_driver : std_logic_vector(0 downto 0) := "0"; + shared variable word_counter: natural := 0; + +begin + + clock_proc: process + begin + for i in 0 to 50 loop + wait for cycle / 2; + clock <= not clock; + end loop; + wait; + end process clock_proc; + + control_unit_inst: entity controller.control_unit + port map( + clk => clock, + rst => reset, + cpu_to_controller => cpu_to_controller, + controller_to_cpu => controller_to_cpu, + socbridge_driver_to_controller => socbridge_driver_to_controller, + controller_to_socbridge_driver => controller_to_socbridge_driver + ); + +stimulus_proc: process +begin + wait for cycle; + + cpu_to_controller.driver_id <= "1"; + socbridge_driver_to_controller.is_active <= '0'; + cpu_to_controller.address <= x"F0F0F0F0"; + cpu_to_controller.seq_mem_access_count <= 3; + cpu_to_controller.cmd <= "01"; + word_counter := 3; + wait for cycle; + current_driver <= "1"; + + report "entering loop with word_counter" & integer'image(word_counter); + for_loop: for i in word_counter - 1 downto 0 loop + wait for cycle; + report "words remaining are " & integer'image(i); + end loop for_loop; + + socbridge_driver_to_controller.is_active <= '0'; + report "Stim process done"; + wait; +end process stimulus_proc; + +monitor_proc: process +begin + + wait for cycle; + + wait for cycle; + assert controller_to_socbridge_driver.request = '1' report "Incorrect driver_id from control_unit" severity error; + assert controller_to_socbridge_driver.address = x"F0F0F0F0" report "Incorrect address from control_unit" severity error; + assert controller_to_socbridge_driver.instruction = WRITE report "Incorrect memory op from control_unit" severity error; + + wait for 5 * cycle; + reset <= '1'; + + report "Monitor process done"; + wait; +end process monitor_proc; + +end architecture tb; diff --git a/src/ganimede/ganimede.vhd b/src/ganimede/ganimede.vhd new file mode 100644 index 0000000..7bcb720 --- /dev/null +++ b/src/ganimede/ganimede.vhd @@ -0,0 +1,74 @@ +library IEEE; +use IEEE.std_logic_1164.all; +library work; +use work.io_types.all; + +entity ganimede is + port ( + clk : in std_logic; + reset : in std_logic; + ext_interface_in : in ext_interface_in_t; + ext_interface_out : out ext_interface_out_t; + int_interface_in : in int_interface_in_t; + int_interface_out : out int_interface_out_t + ); +end entity ganimede; +architecture rtl of ganimede is + --- SIGNAL DECLERATIONS --- + signal gan_int_interface_in : int_interface_in_t; + signal gan_int_interface_out : int_interface_out_t; + signal gan_ext_interface_in : ext_interface_in_t; + signal gan_ext_interface_out : ext_interface_out_t; + + --signal gan_socbridge_WE_in : std_logic; + --signal gan_socbridge_WE_out : std_logic; + --signal gan_socbridge_is_full_in : std_logic; + --signal gan_socbridge_is_full_out : std_logic; + + --- COMPONENT DECLERATIONS --- + --component fifo is + -- generic( + -- WIDTH : positive; + -- DEPTH : positive + -- ); + -- port( + -- clk, reset, read_enable, write_enable : in std_logic; + -- is_full, is_empty : out std_logic; + -- data_in : in std_logic_vector(WIDTH - 1 downto 0); + -- data_out : out std_logic_vector(WIDTH - 1 downto 0) + -- ); + --end component; + + component socbridge_driver is + port( + clk : in std_logic; + reset : in std_logic; + ext_in : in ext_to_socbridge_driver_t; + ext_out : out socbridge_driver_to_ext_t; + int_in : out buffer_to_socbridge_driver_t; + int_out : in socbridge_driver_to_buffer_t + ); + end component; + +begin + --- CONNECT EXTERNAL SIGNALS TO INTERNAL CONNECTIONS --- + gan_int_interface_in <= int_interface_in; + int_interface_out <= gan_int_interface_out; + gan_ext_interface_in <= ext_interface_in; + ext_interface_out <= gan_ext_interface_out; + + --- DRIVER INSTANTIATION --- + socbridge_driver_inst: socbridge_driver + port map( + clk => clk, + reset => reset, + ext_in => gan_ext_interface_in.socbridge, + ext_out => gan_ext_interface_out.socbridge, + int_in => gan_int_interface_in.socbridge, + int_out => gan_int_interface_out.socbridge + ); + + --- LATER WE ADD OPTIMIZATIONS HERE --- + + +end architecture rtl; diff --git a/src/ganimede/io_type_pkg.vhd b/src/ganimede/io_type_pkg.vhd new file mode 100644 index 0000000..ed67ad3 --- /dev/null +++ b/src/ganimede/io_type_pkg.vhd @@ -0,0 +1,102 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.MATH_REAL.all; + +package io_types is + + --- CONSTANTS --- + constant number_of_drivers: natural := 1; + constant address_width: natural := 32; + constant seq_vector_length: natural := 8; + constant inst_word_width: natural := 2; + + --- STANDARD TYPES --- + type instruction_command_t is (NO_OP, READ, WRITE); + + type ext_protocol_def_t is record + name: string (1 to 20); + payload_width: natural; + control_width_in, control_width_out: natural; + end record ext_protocol_def_t; + + type interface_inst_t is record + socbridge: ext_protocol_def_t; + end record interface_inst_t; + + --- CONTROL UNIT --- + type cpu_to_controller_t is record + driver_id: std_logic_vector(number_of_drivers - 1 downto 0); + address: std_logic_vector(address_width - 1 downto 0); + seq_mem_access_count: integer; + cmd: std_logic_vector(1 downto 0); + end record cpu_to_controller_t; + + type controller_to_cpu_t is record + ready: std_logic; + end record controller_to_cpu_t; + + --type controller_to_socbridge_driver_t is record + -- driver_id: std_logic_vector(number_of_drivers - 1 downto 0); + -- address: std_logic_vector(address_width - 1 downto 0); + -- seq_mem_access_count: integer; + -- instruction: instruction_command_t; + --end record controller_to_socbridge_driver_t; + + --type socbridge_driver_to_controller_t is record + -- active_driver: std_logic_vector(number_of_drivers - 1 downto 0); + --end record socbridge_driver_to_controller_t; + + type socbridge_driver_to_controller_t is record + is_active : std_logic; + end record socbridge_driver_to_controller_t; + + type controller_to_socbridge_driver_t is record + request: std_logic; + address: std_logic_vector(address_width - 1 downto 0); + seq_mem_access_count: integer; + instruction: instruction_command_t; + end record controller_to_socbridge_driver_t; + + --- PROTOCOL INFORMATION --- + constant interface_inst : interface_inst_t := ( + socbridge => ("SoCBridge ", 8, 2, 2) + ); + + --- AUTOGENERATED TYPES --- + type ext_to_socbridge_driver_t is record + payload : STD_LOGIC_VECTOR(interface_inst.socbridge.payload_width - 1 downto 0); + control : STD_LOGIC_VECTOR(interface_inst.socbridge.control_width_in - 1 downto 0); + end record ext_to_socbridge_driver_t; + + type socbridge_driver_to_ext_t is record + payload : STD_LOGIC_VECTOR(interface_inst.socbridge.payload_width - 1 downto 0); + control : STD_LOGIC_VECTOR(interface_inst.socbridge.control_width_in - 1 downto 0); + end record socbridge_driver_to_ext_t; + + type socbridge_driver_to_buffer_t is record + payload : STD_LOGIC_VECTOR(interface_inst.socbridge.payload_width - 1 downto 0); + write_enable_in, is_full_out : std_logic; + end record socbridge_driver_to_buffer_t; + + type buffer_to_socbridge_driver_t is record + payload : STD_LOGIC_VECTOR(interface_inst.socbridge.payload_width - 1 downto 0); + write_enable_out, is_full_in : std_logic; + end record buffer_to_socbridge_driver_t; + + type ext_interface_in_t is record + socbridge : ext_to_socbridge_driver_t; + end record ext_interface_in_t; + + type ext_interface_out_t is record + socbridge : socbridge_driver_to_ext_t; + end record ext_interface_out_t; + + type int_interface_out_t is record + socbridge : socbridge_driver_to_buffer_t; + end record int_interface_out_t; + + type int_interface_in_t is record + socbridge : buffer_to_socbridge_driver_t; + end record int_interface_in_t; + +end package io_types; diff --git a/src/io_type_pkg.vhd b/src/io_type_pkg.vhd deleted file mode 100644 index 7f025df..0000000 --- a/src/io_type_pkg.vhd +++ /dev/null @@ -1,67 +0,0 @@ -library IEEE; -use IEEE.std_logic_1164.all; -use IEEE.MATH_REAL.all; - -package io_types is - - --- STANDARD TYPES --- - type ext_protocol_impl_t is record - payload, control: STD_LOGIC_VECTOR; - end record ext_protocol_impl_t; - - type int_protocol_impl_t is record - payload : STD_LOGIC_VECTOR; - -- ADD MORE STUFF WHEN WE HAVE DECIDED UPON DRIVER INTERFACE - end record int_protocol_impl_t; - - type ext_protocol_def_t is record - name: string (1 to 20); - payload_width: natural; - control_width_in, control_width_out: natural; - end record ext_protocol_def_t; - - type interface_inst_t is record - socbridge: ext_protocol_def_t; - spi: ext_protocol_def_t; - end record interface_inst_t; - - --- PROTOCOL INFORMATION --- - constant interface_inst : interface_inst_t := ( - ("SoCBridge ", 8, 2, 2), - ("SPI ", 1, 3, 3) - ); - - --- AUTOGENERATED TYPES --- - type ext_interface_in_t is record - socbridge : ext_protocol_impl_t( - payload(interface_inst.socbridge.payload_width - 1 downto 0), - control(interface_inst.socbridge.control_width_in - 1 downto 0)); - spi : ext_protocol_impl_t( - payload(interface_inst.spi.payload_width - 1 downto 0), - control(interface_inst.spi.control_width_in - 1 downto 0)); - end record ext_interface_in_t; - - type ext_interface_out_t is record - socbridge : ext_protocol_impl_t( - payload(interface_inst.socbridge.payload_width - 1 downto 0), - control(interface_inst.socbridge.control_width_out - 1 downto 0)); - spi : ext_protocol_impl_t( - payload(interface_inst.spi.payload_width - 1 downto 0), - control(interface_inst.spi.control_width_out - 1 downto 0)); - end record ext_interface_out_t; - - type int_interface_in_t is record - socbridge : int_protocol_impl_t( - payload(interface_inst.socbridge.payload_width - 1 downto 0)); - spi : int_protocol_impl_t( - payload(interface_inst.spi.payload_width - 1 downto 0)); - end record int_interface_in_t; - - type int_interface_out_t is record - socbridge : int_protocol_impl_t( - payload(interface_inst.socbridge.payload_width - 1 downto 0)); - spi : int_protocol_impl_t( - payload(interface_inst.spi.payload_width - 1 downto 0)); - end record int_interface_out_t; - -end package io_types; diff --git a/src/socbridge/socbridge_driver.vhd b/src/socbridge/socbridge_driver.vhd new file mode 100644 index 0000000..449a32e --- /dev/null +++ b/src/socbridge/socbridge_driver.vhd @@ -0,0 +1,398 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.NUMERIC_STD.all; +library ganimede; +use ganimede.io_types.all; +library socbridge; +use socbridge.socbridge_driver_tb_pkg.all; + + +entity socbridge_driver is + port( + clk : in std_logic; + rst : in std_logic; + controller_to_socbridge_driver : in controller_to_socbridge_driver_t; + socbridge_driver_to_controller : out socbridge_driver_to_controller_t; + ext_to_socbridge_driver : in ext_to_socbridge_driver_t; + socbridge_driver_to_ext : out socbridge_driver_to_ext_t; + socbridge_driver_to_buffer : out socbridge_driver_to_buffer_t; + buffer_to_socbridge_driver : in buffer_to_socbridge_driver_t + ); +end entity socbridge_driver; + +architecture rtl of socbridge_driver is + + signal next_parity_out : std_logic; + signal ext_to_socbridge_driver_rec : ext_protocol_t; + shared variable socbridge_driver_to_ext_data_cmd : std_logic_vector(interface_inst.socbridge.payload_width - 1 downto 0); + signal test : std_logic_vector(interface_inst.socbridge.payload_width - 1 downto 0); + signal next_cmd : command_t; + signal next_cmd_size : integer; + signal next_state : state_t; + signal curr_cmd_bits : std_logic_vector(4 downto 0); + signal curr_response : response_t; + signal curr_response_bits : std_logic_vector(4 downto 0); + signal st : state_rec_t; + --- TRANSLATOR --- + signal trans_st : translator_state_rec_t; + signal trans_next_state : translator_state_t; +begin + --- DEBUG GLOBAL BINDINGS --- + -- synthesis translate_off + G_next_parity_out <= next_parity_out; + G_ext_to_socbridge_driver_rec <= ext_to_socbridge_driver_rec; + G_next_state <= next_state; + G_socbridge_driver_to_ext_data_cmd <=test; + G_curr_command_bits <= curr_cmd_bits; + G_curr_response <= curr_response; + G_curr_response_bits <= curr_response_bits; + G_st <= st; + G_trans_st <= trans_st; + -- synthesis translate_on + ext_to_socbridge_driver_rec.data <= ext_to_socbridge_driver.payload; + ext_to_socbridge_driver_rec.clk <= ext_to_socbridge_driver.control(1); + ext_to_socbridge_driver_rec.parity <= ext_to_socbridge_driver.control(0); + + -- Helpful Bindings -- + curr_response_bits <= ext_to_socbridge_driver.payload(7 downto 3); -- CANT USE ext_to_socbridge_driver_REC here for some reason, the assignment becomes stasteful + -- Not sure that the two process method is helping here: if this was a normal + -- signal assignment there would be no confusion. + -- in the case ... <= ext_to_socbridge_driver_rec we get + -- curr_resp | ext_to_socbridge_driver_rec | ext_to_socbridge_driver + -- 00000 | 00000000 | 00001001 + -- 00000 | 00001001 | 00001001 + -- 00001 | 00001001 | 00001001 + -- 00001 | 00001001 | 00001001 + -- + -- but in the case ... <= ext_to_socbridge_driver we get + -- curr_resp | ext_to_socbridge_driver_rec | ext_to_socbridge_driver + -- 00000 | 00000000 | 00001001 + -- 00001 | 00001001 | 00001001 + -- 00001 | 00001001 | 00001001 + -- 00001 | 00001001 | 00001001 + + with curr_response_bits select + curr_response <= WRITE_ACK when "00001", + WRITE_ACK when "00101", + READ_RESPONSE when "01000", + READ_RESPONSE when "01100", + NO_OP when others; + comb_proc: process(ext_to_socbridge_driver, buffer_to_socbridge_driver, curr_response, st, controller_to_socbridge_driver, trans_st) + begin + -- Outputs + socbridge_driver_to_ext <= create_io_type_out_from_ext_protocol(st.socbridge_driver_to_ext_reg); + with trans_st.curr_state select + socbridge_driver_to_controller.is_active <= '0' when IDLE, + '1' when others; + + --- State Transition Diagram --- +-- +-- +-- +-- +-----+ +-- | | +-- \|/ /--+ +-- IDLE<-------------------+ +-- / \ | +-- / \ | +-- / \ | +-- \|/ \|/ | +-- TX_HEADER RX_HEADER | +-- |\ / | | +-- | \ / | | +-- | ADDR1 | | +-- | | | | +-- | \|/ | | +-- | ADDR2 | | +-- | | | | +-- | \|/ | | +-- | ADDR3 | | +-- | | | | +-- | \|/ | | +-- | ADDR4 | | +-- | /\ | | +-- | / \ | | +-- |-+ +----| +---+ | +-- \|/ \|/ \|/ | | +-- TX_BODY RX_RESPONSE---+ | +-- | | | +-- | +--+ | | +-- \|/\|/ | \|/ | +-- TX_ACK--+ RX_BODY | +-- | | | +-- | | | +-- +-----------+--------------+ +-- + --- Next State Assignment --- + case st.curr_state is + when IDLE => + if st.curr_cmd = WRITE or st.curr_cmd = WRITE_ADD then + next_state <= TX_HEADER; + elsif st.curr_cmd = READ or st.curr_cmd = READ_ADD then + next_state <= RX_HEADER; + else + next_state <= IDLE; + end if; + when TX_HEADER => + -- The header only takes one word (cycle) to transmit. + -- Continue to body or address directly afterwards. + if st.curr_cmd = WRITE_ADD then + next_state <= ADDR1; + else + next_state <= TX_BODY; + end if; + when TX_BODY => + -- Here we want to stay in TX_BODY for the duration of a packet. + if st.write_stage = 0 then + next_state <= TX_ACK; + else + next_state <= TX_BODY; + end if; + when TX_ACK => + -- Wait for write acknowledgement. + if curr_response = WRITE_ACK then + next_state <= IDLE; + else + next_state <= TX_ACK; + end if; + when RX_HEADER => + -- The header only takes one word (cycle) to transmit. + -- Continue to awaiting response directly afterwards. + if st.curr_cmd = READ_ADD then + next_state <= ADDR1; + else + next_state <= RX_RESPONSE; + end if; + when RX_RESPONSE => + -- Wait for read response. + if curr_response = READ_RESPONSE then + next_state <= RX_BODY; + else + next_state <= RX_RESPONSE; + end if; + when RX_BODY => + -- Here we want to stay in RX_BODY for the duration of a packet. + if st.read_stage = 0 then + next_state <= IDLE; + else + next_state <= RX_BODY; + end if; + when ADDR1 => + -- Transmits the entire address and returns to the appropriate + next_state <= ADDR2; + when ADDR2 => + next_state <= ADDR3; + when ADDR3 => + next_state <= ADDR4; + when ADDR4 => + if st.curr_cmd = WRITE or st.curr_cmd = WRITE_ADD then + next_state <= TX_BODY; + else + next_state <= RX_RESPONSE; + end if; + end case; + + --- Combinatorial output based on current state --- + socbridge_driver_to_ext_data_cmd := (others => '0'); + socbridge_driver_to_buffer.is_full_out <= '1'; + socbridge_driver_to_buffer.write_enable_in <= '0'; + socbridge_driver_to_buffer.payload <= (others => '0'); + case st.curr_state is + when IDLE => + if st.curr_cmd = WRITE or st.curr_cmd = WRITE_ADD then + socbridge_driver_to_ext_data_cmd := get_cmd_bits(st.curr_cmd) & get_size_bits(st.curr_cmd_size); + elsif st.curr_cmd = READ or st.curr_cmd = READ_ADD then + socbridge_driver_to_ext_data_cmd := get_cmd_bits(st.curr_cmd) & get_size_bits(st.curr_cmd_size); + else + end if; + when TX_HEADER => + if st.curr_cmd = WRITE_ADD then + socbridge_driver_to_ext_data_cmd := st.curr_addr(7 downto 0); + else + socbridge_driver_to_ext_data_cmd := buffer_to_socbridge_driver.payload; + socbridge_driver_to_buffer.is_full_out <= '0'; + end if; + when TX_BODY => + if st.write_stage > 0 then + socbridge_driver_to_buffer.is_full_out <= '0'; + socbridge_driver_to_ext_data_cmd := buffer_to_socbridge_driver.payload; + else + socbridge_driver_to_ext_data_cmd := (others => '0'); + end if; + when TX_ACK => + when RX_HEADER => + if st.curr_cmd = READ_ADD then + socbridge_driver_to_ext_data_cmd := st.curr_addr(7 downto 0); + end if; + when RX_RESPONSE => + when RX_BODY => + socbridge_driver_to_buffer.payload <= st.ext_to_socbridge_driver_reg.data; + socbridge_driver_to_buffer.write_enable_in <= '1'; + when ADDR1 => + socbridge_driver_to_ext_data_cmd := st.curr_addr(15 downto 8); + when ADDR2 => + socbridge_driver_to_ext_data_cmd := st.curr_addr(23 downto 16); + when ADDR3 => + socbridge_driver_to_ext_data_cmd := st.curr_addr(31 downto 24); + when ADDR4 => + if st.curr_cmd = WRITE_ADD then + socbridge_driver_to_buffer.is_full_out <= '0'; + socbridge_driver_to_ext_data_cmd := buffer_to_socbridge_driver.payload; + report integer'image(to_integer(signed(socbridge_driver_to_ext_data_cmd))) & " "& integer'image(to_integer(signed(buffer_to_socbridge_driver.payload))); + end if; + end case; + next_parity_out <= calc_parity(socbridge_driver_to_ext_data_cmd); + --- DEBUG GLOBAL BINDINGS --- + -- synthesis translate_off + test <= socbridge_driver_to_ext_data_cmd; + -- synthesis translate_on + --- TRANSLATOR --- + + --- Next state assignment + case trans_st.curr_state is + when IDLE => + if trans_st.curr_inst.request = '1' then + trans_next_state <= SEND; + else + trans_next_state <= IDLE; + end if; + -- Wait for driver to go idle and send next instruction. Then enter AWAIT + when SEND => + if st.curr_state /= IDLE then + trans_next_state <= SEND_ACCEPTED; + else + trans_next_state <= SEND; + end if; + -- Transisitonal state to decrement counter in transition between SEND and AWAIT. + when SEND_ACCEPTED => + trans_next_state <= AWAIT; + -- Wait for driver to finish current instruction, then reenter SEND + when AWAIT => + if trans_st.curr_inst.seq_mem_access_count <= 0 and st.curr_state = IDLE then + trans_next_state <= IDLE; + elsif st.curr_state = IDLE then + trans_next_state <= SEND; + else + trans_next_state <= AWAIT; + end if; + end case; + + --- Combinatorial output based on state + next_cmd <= NO_OP; + next_cmd_size <= 0; + case trans_st.curr_state is + when IDLE => + when SEND => + if trans_st.is_first_word = '1' then + if trans_st.curr_inst.instruction = READ then + next_cmd <= READ_ADD; + elsif trans_st.curr_inst.instruction = WRITE then + next_cmd <= WRITE_ADD; + end if; + else + if trans_st.curr_inst.instruction = READ then + next_cmd <= READ; + elsif trans_st.curr_inst.instruction = WRITE then + next_cmd <= WRITE; + end if; + end if; + + if trans_st.curr_inst.seq_mem_access_count > 128 then + next_cmd_size <= 128; + elsif trans_st.curr_inst.seq_mem_access_count > 0 then + next_cmd_size <= trans_st.curr_inst.seq_mem_access_count; + else + next_cmd_size <= 0; + end if; + when others => + end case; + + end process comb_proc; + -- Process updating internal registers based on primary clock + seq_proc: process(ext_to_socbridge_driver_rec.clk, rst, clk) + begin + if(rst = '1') then + st.ext_to_socbridge_driver_reg.data <= (others => '0'); + st.socbridge_driver_to_ext_reg.data <= (others => '0'); + st.socbridge_driver_to_ext_reg.clk <= '0'; + st.socbridge_driver_to_ext_reg.parity <= '1'; + st.curr_state <= IDLE; + st.write_stage <= 0; + st.read_stage <= 0; + st.curr_cmd <= NO_OP; + st.curr_cmd_size <= 0; + st.curr_addr <= (others => '0'); + + elsif(rising_edge(ext_to_socbridge_driver_rec.clk)) then + st.ext_to_socbridge_driver_reg.data <= ext_to_socbridge_driver_rec.data; + st.ext_to_socbridge_driver_reg.clk <= ext_to_socbridge_driver_rec.clk; + st.ext_to_socbridge_driver_reg.parity <= ext_to_socbridge_driver_rec.parity; + st.socbridge_driver_to_ext_reg.data <= socbridge_driver_to_ext_data_cmd; + st.socbridge_driver_to_ext_reg.clk <= not st.socbridge_driver_to_ext_reg.clk; + st.socbridge_driver_to_ext_reg.parity <= next_parity_out; + st.curr_state <= next_state; + case st.curr_state is + when IDLE => + st.curr_cmd <= next_cmd; + st.curr_cmd_size <= next_cmd_size; + st.curr_addr <= trans_st.curr_inst.address; + if next_cmd_size > 0 then + st.write_stage <= next_cmd_size - 1; + st.read_stage <= next_cmd_size - 1; + end if; + when TX_HEADER => + when TX_BODY => + if st.write_stage > 0 then + st.write_stage <= st.write_stage - 1; + end if; + when TX_ACK => + st.curr_cmd <= NO_OP; + st.curr_cmd_size <= 0; + when RX_HEADER => + when RX_BODY => + if st.read_stage > 0 then + st.read_stage <= st.read_stage - 1; + else + st.curr_cmd <= NO_OP; + st.curr_cmd_size <= 0; + end if; + + when others => + end case; + end if; + + --- TRANSLATOR --- + + if(rst = '1') then + trans_st.curr_state <= IDLE; + trans_st.curr_inst.request <= '0'; + trans_st.curr_inst.address <= (others => '0'); + trans_st.curr_inst.seq_mem_access_count <= 0; + trans_st.curr_inst.instruction <= NO_OP; + trans_st.is_first_word <= '1'; + elsif(rising_edge(clk)) then + trans_st.curr_state <= trans_next_state; + case trans_st.curr_state is + when IDLE => + if controller_to_socbridge_driver.request = '1' then + trans_st.curr_inst <= controller_to_socbridge_driver; + else + end if; + trans_st.is_first_word <= '1'; + when SEND => + when SEND_ACCEPTED => + trans_st.curr_inst.seq_mem_access_count <= trans_st.curr_inst.seq_mem_access_count - 128; + when AWAIT => + if trans_st.curr_inst.seq_mem_access_count <= 0 and st.curr_state = IDLE then + trans_st.curr_inst.request <= '0'; + trans_st.curr_inst.address <= (others => '0'); + trans_st.curr_inst.seq_mem_access_count <= 0; + trans_st.curr_inst.instruction <= NO_OP; + end if; + trans_st.is_first_word <= '0'; + when others => + end case; + end if; + end process seq_proc; + +end architecture rtl; diff --git a/src/socbridge/socbridge_driver_tb.gtkw b/src/socbridge/socbridge_driver_tb.gtkw new file mode 100644 index 0000000..4b32853 --- /dev/null +++ b/src/socbridge/socbridge_driver_tb.gtkw @@ -0,0 +1,77 @@ +[*] +[*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI +[*] Thu Feb 27 10:27:13 2025 +[*] +[dumpfile] "/home/thesis1/repos/exjobb-public/src/wave/socbridge_driver_tb-tb.ghw" +[dumpfile_mtime] "Thu Feb 27 10:26:19 2025" +[dumpfile_size] 2417 +[savefile] "/home/thesis1/repos/exjobb-public/src/socbridge_driver_tb.gtkw" +[timestart] 21800000 +[size] 956 1033 +[pos] -1 -1 +*-24.456779 22000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] top. +[treeopen] top.socbridge_driver_tb. +[treeopen] top.socbridge_driver_tb_pkg. +[treeopen] top.socbridge_driver_tb_pkg.g_st. +[sst_width] 273 +[signals_width] 214 +[sst_expanded] 1 +[sst_vpaned_height] 324 +@800200 +-Outwards +-Internal +@28 +top.socbridge_driver_tb.int_out.is_full_in +@22 +#{top.socbridge_driver_tb.int_out.payload[7:0]} top.socbridge_driver_tb.int_out.payload[7] top.socbridge_driver_tb.int_out.payload[6] top.socbridge_driver_tb.int_out.payload[5] top.socbridge_driver_tb.int_out.payload[4] top.socbridge_driver_tb.int_out.payload[3] top.socbridge_driver_tb.int_out.payload[2] top.socbridge_driver_tb.int_out.payload[1] top.socbridge_driver_tb.int_out.payload[0] +@28 +top.socbridge_driver_tb.int_out.write_enable_out +@1000200 +-Internal +@800200 +-External +@28 ++{clk} top.socbridge_driver_tb.ext_out.control[1] ++{parity} top.socbridge_driver_tb.ext_out.control[0] ++{next_parity} top.socbridge_driver_tb_pkg.g_next_parity_out +@22 +#{top.socbridge_driver_tb.ext_out.payload[7:0]} top.socbridge_driver_tb.ext_out.payload[7] top.socbridge_driver_tb.ext_out.payload[6] top.socbridge_driver_tb.ext_out.payload[5] top.socbridge_driver_tb.ext_out.payload[4] top.socbridge_driver_tb.ext_out.payload[3] top.socbridge_driver_tb.ext_out.payload[2] top.socbridge_driver_tb.ext_out.payload[1] top.socbridge_driver_tb.ext_out.payload[0] ++{next_payload[7:0]} #{top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[7:0]} top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[7] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[6] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[5] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[4] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[3] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[2] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[1] top.socbridge_driver_tb_pkg.g_ext_out_data_cmd[0] +@1000200 +-External +-Outwards +@800200 +-Inwards +-Internal +@28 +top.socbridge_driver_tb.int_in.is_full_out +@22 +#{top.socbridge_driver_tb.int_in.payload[7:0]} top.socbridge_driver_tb.int_in.payload[7] top.socbridge_driver_tb.int_in.payload[6] top.socbridge_driver_tb.int_in.payload[5] top.socbridge_driver_tb.int_in.payload[4] top.socbridge_driver_tb.int_in.payload[3] top.socbridge_driver_tb.int_in.payload[2] top.socbridge_driver_tb.int_in.payload[1] top.socbridge_driver_tb.int_in.payload[0] +@28 +top.socbridge_driver_tb.int_in.write_enable_in +@1000200 +-Internal +@800200 +-External +@28 ++{clk} top.socbridge_driver_tb.ext_in.control[1] ++{parity} top.socbridge_driver_tb.ext_in.control[0] +@22 +#{top.socbridge_driver_tb.ext_in.payload[7:0]} top.socbridge_driver_tb.ext_in.payload[7] top.socbridge_driver_tb.ext_in.payload[6] top.socbridge_driver_tb.ext_in.payload[5] top.socbridge_driver_tb.ext_in.payload[4] top.socbridge_driver_tb.ext_in.payload[3] top.socbridge_driver_tb.ext_in.payload[2] top.socbridge_driver_tb.ext_in.payload[1] top.socbridge_driver_tb.ext_in.payload[0] +@1000200 +-External +-Inwards +@800200 +-Internal Signals +@420 +top.socbridge_driver_tb_pkg.g_st.curr_state ++{next_state} top.socbridge_driver_tb_pkg.g_next_state +top.socbridge_driver_tb_pkg.g_curr_command +top.socbridge_driver_tb_pkg.g_curr_respoonse +@1000200 +-Internal Signals +@420 +top.socbridge_driver_tb.cmd +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/src/socbridge/socbridge_driver_tb.vhd b/src/socbridge/socbridge_driver_tb.vhd new file mode 100644 index 0000000..0471486 --- /dev/null +++ b/src/socbridge/socbridge_driver_tb.vhd @@ -0,0 +1,332 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.MATH_REAL.all; +library work; +use work.socbridge_driver_tb_pkg.all; +library ganimede; +use ganimede.io_types.all; +library socbridge; + + +entity socbridge_driver_tb is +end entity socbridge_driver_tb; + +architecture tb of socbridge_driver_tb is + signal clk : std_logic := '0'; + signal rst : std_logic; + signal cmd : command_t; + signal address : std_logic_vector(31 downto 0); + signal cmd_size : positive; + signal ext_to_socbridge_driver : ext_to_socbridge_driver_t; + signal socbridge_driver_to_ext : socbridge_driver_to_ext_t; + signal buffer_to_socbridge_driver : buffer_to_socbridge_driver_t; + signal socbridge_driver_to_buffer : socbridge_driver_to_buffer_t; + signal controller_to_socbridge_driver : controller_to_socbridge_driver_t; + signal socbridge_driver_controller : socbridge_driver_to_controller_t; + signal curr_word : std_logic_vector(ext_to_socbridge_driver.payload'length - 1 downto 0); + signal expected_out : std_logic_vector(socbridge_driver_to_ext.payload'length - 1 downto 0); + + constant CLK_PERIOD : TIME := 10 ns; + constant SIMULATION_CYCLE_COUNT : INTEGER := 100; + + procedure fail(error_msg : string) is + begin + wait for CLK_PERIOD; + report "Simulation ending due to: " & error_msg & ". Shutting down..." severity FAILURE; + end procedure; + + procedure check_next_state(correct_state: state_t) is + begin + if(not (correct_state = G_next_state)) then + report "Next State is not what was expected, found " & state_t'image(G_next_state) + & " but expected " & state_t'image(correct_state) severity error; + fail("Next State"); + end if; + end procedure; + + procedure check_data_out(correct_data: std_logic_vector(socbridge_driver_to_ext.payload'length - 1 downto 0)) is + begin + if(not (correct_data = socbridge_driver_to_ext.payload)) then + report "Data out is not what was expected, found " & to_string(socbridge_driver_to_ext.payload) + & " but expected " & to_string(correct_data) severity error; + fail("Data out"); + end if; + end procedure; + + procedure check_parity(correct_data: std_logic_vector(socbridge_driver_to_ext.payload'length - 1 downto 0)) is + begin + if(not (calc_parity(correct_data) = calc_parity(socbridge_driver_to_ext.payload))) then + report "Parity out is not what was expected, found " & std_logic'image(calc_parity(socbridge_driver_to_ext.payload)) + & " but expected " & std_logic'image(calc_parity(correct_data)) severity error; + fail("Parity out"); + end if; + end procedure; + +-- component socbridge_driver is +-- port( +-- clk : in std_logic; +-- rst : in std_logic; +-- cmd : in command_t; +-- address : in std_logic_vector(31 downto 0); +-- cmd_size: in positive; +-- ext_to_socbridge_driver : in ext_to_socbridge_driver_t; +-- socbridge_driver_to_ext : out socbridge_driver_to_ext_t; +-- buffer_to_socbridge_driver : out buffer_to_socbridge_driver_t; +-- socbridge_driver_to_buffer : in socbridge_driver_to_buffer_t +-- ); +-- end component socbridge_driver; + +begin + socbridge_driver_inst: entity socbridge.socbridge_driver + port map( + clk => clk, + rst => rst, + controller_to_socbridge_driver => controller_to_socbridge_driver, + socbridge_driver_to_controller => socbridge_driver_controller, + ext_to_socbridge_driver => ext_to_socbridge_driver, + socbridge_driver_to_ext => socbridge_driver_to_ext, + buffer_to_socbridge_driver => buffer_to_socbridge_driver, + socbridge_driver_to_buffer => socbridge_driver_to_buffer + ); + + ext_to_socbridge_driver.control(1) <= clk; + real_clk_proc: process + begin + for x in 0 to SIMULATION_CYCLE_COUNT*2 loop + clk <= not clk; + wait for CLK_PERIOD / 2; + end loop; + wait; + end process real_clk_proc; + + verify_clk: process + variable last_clk : std_logic; + begin + wait for CLK_PERIOD / 2; + for x in 0 to SIMULATION_CYCLE_COUNT loop + if last_clk = socbridge_driver_to_ext.control(1) then + report "Secondary side clk not correct." severity error; + end if; + last_clk := socbridge_driver_to_ext.control(1); + wait for CLK_PERIOD; + end loop; + wait; + end process verify_clk; + + verify_out_signals: process + begin + wait for CLK_PERIOD / 2; + for x in 0 to SIMULATION_CYCLE_COUNT loop + check_data_out(expected_out); + check_parity(expected_out); + wait for CLK_PERIOD; + end loop; + wait; + end process verify_out_signals; + + verify_signals : process + begin + expected_out <= "00000000"; + wait for 3 * CLK_PERIOD; + wait for CLK_PERIOD / 3; + expected_out <= "00000000"; + check_next_state(IDLE); + wait for CLK_PERIOD /4; + check_next_state(TX_HEADER); + wait for CLK_PERIOD * 3 / 4; + expected_out <= get_cmd_bits(WRITE) & get_size_bits_sim(2); + check_next_state(TX_BODY); + wait for CLK_PERIOD; + expected_out <= "00000001"; + check_next_state(TX_BODY); + wait for CLK_PERIOD; + expected_out <= "00000010"; + check_next_state(TX_ACK); + wait for CLK_PERIOD; + expected_out <= "00000000"; + check_next_state(TX_ACK); + wait for CLK_PERIOD; + check_next_state(IDLE); + wait for CLK_PERIOD * 6; + expected_out <= "00000000"; + check_next_state(IDLE); + wait for CLK_PERIOD /4; + check_next_state(TX_HEADER); + wait for CLK_PERIOD * 3 / 4; + expected_out <= get_cmd_bits(WRITE_ADD) & get_size_bits(2); + check_next_state(ADDR1); + wait for CLK_PERIOD; + expected_out <= x"FA"; + check_next_state(ADDR2); + wait for CLK_PERIOD; + expected_out <= x"A0"; + check_next_state(ADDR3); + wait for CLK_PERIOD; + expected_out <= x"0F"; + check_next_state(ADDR4); + wait for CLK_PERIOD; + expected_out <= x"FA"; + check_next_state(TX_BODY); + wait for CLK_PERIOD; + expected_out <= "00000100"; + check_next_state(TX_BODY); + wait for CLK_PERIOD; + expected_out <= "00001000"; + check_next_state(TX_ACK); + wait for CLK_PERIOD; + expected_out <= "00000000"; + check_next_state(TX_ACK); + wait for CLK_PERIOD; + expected_out <= "00000000"; + check_next_state(IDLE); + wait for CLK_PERIOD * 2; + wait for CLK_PERIOD /4; + check_next_state(RX_HEADER); + wait for CLK_PERIOD * 3 / 4; + expected_out <= get_cmd_bits(READ) & get_size_bits(2); + check_next_state(RX_RESPONSE); + wait for CLK_PERIOD; + expected_out <= "00000000"; + check_next_state(RX_RESPONSE); + wait for CLK_PERIOD; + check_next_state(RX_BODY); + wait for CLK_PERIOD; + check_next_state(RX_BODY); + wait for CLK_PERIOD; + check_next_state(IDLE); + wait for CLK_PERIOD * 5; + wait for CLK_PERIOD /4; + check_next_state(RX_HEADER); + wait for CLK_PERIOD * 3 / 4; + expected_out <= get_cmd_bits(READ_ADD) & get_size_bits(2); + check_next_state(ADDR1); + wait for CLK_PERIOD; + expected_out <= x"FA"; + check_next_state(ADDR2); + wait for CLK_PERIOD; + expected_out <= x"A0"; + check_next_state(ADDR3); + wait for CLK_PERIOD; + expected_out <= x"0F"; + check_next_state(ADDR4); + wait for CLK_PERIOD; + expected_out <= x"FA"; + check_next_state(RX_RESPONSE); + wait for CLK_PERIOD; + expected_out <= "00000000"; + check_next_state(RX_RESPONSE); + wait for CLK_PERIOD; + check_next_state(RX_BODY); + wait for CLK_PERIOD; + check_next_state(RX_BODY); + wait for CLK_PERIOD; + check_next_state(IDLE); + wait; + end process verify_signals; + + command_stimulus: process + begin + cmd <= NO_OP; + cmd_size <= 2; + wait for 3*CLK_PERIOD; + wait for CLK_PERIOD / 2; + cmd <= WRITE; + wait for CLK_PERIOD; + cmd <= NO_OP; + wait for CLK_PERIOD * 10; + cmd <= WRITE_ADD; + address <= x"FA0FA0FA"; + wait for CLK_PERIOD; + cmd <= NO_OP; + address <= (others => '0'); + wait for CLK_PERIOD * 10; + cmd <= READ; + wait for CLK_PERIOD; + cmd <= NO_OP; + wait for CLK_PERIOD * 10; + cmd <= READ_ADD; + address <= x"FA0FA0FA"; + wait for CLK_PERIOD; + cmd <= NO_OP; + address <= (others => '0'); + wait; + end process command_stimulus; + + external_stimulus_signal: process(curr_word) + begin + ext_to_socbridge_driver.payload <= curr_word; + ext_to_socbridge_driver.control(0) <= calc_parity(curr_word); + end process external_stimulus_signal; + + external_stimulus: process + begin + rst <= '0'; + wait for CLK_PERIOD / 1000; + rst <= '1'; + curr_word <= "00000000"; + wait for 999 * CLK_PERIOD / 1000; + wait for 2 * CLK_PERIOD; + rst <= '0'; + wait for CLK_PERIOD / 2; + wait for 4* CLK_PERIOD; + curr_word <= "00001001"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + wait for CLK_PERIOD * 14; + curr_word <= "00101001"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + wait for CLK_PERIOD*5; + curr_word <= "01000001"; + wait for CLK_PERIOD; + curr_word <= "10000000"; + wait for CLK_PERIOD; + curr_word <= "01000000"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + wait for CLK_PERIOD*12; + curr_word <= "01100001"; + wait for CLK_PERIOD; + curr_word <= "00100000"; + wait for CLK_PERIOD; + curr_word <= "00010000"; + wait for CLK_PERIOD; + curr_word <= "00000000"; + + + wait; + end process external_stimulus; + + internal_stimulus: process + begin + buffer_to_socbridge_driver.is_full_in <= '0'; + buffer_to_socbridge_driver.write_enable_out <= '0'; + wait for 3 * CLK_PERIOD; + -- stimulus goes here + buffer_to_socbridge_driver.write_enable_out <= '1'; + buffer_to_socbridge_driver.payload <= "00000001"; + wait until rising_edge(clk) and socbridge_driver_to_buffer.is_full_out = '0'; + wait until falling_edge(clk); + buffer_to_socbridge_driver.payload <= "00000010"; + wait until rising_edge(clk) and socbridge_driver_to_buffer.is_full_out = '0'; + wait until falling_edge(clk); + buffer_to_socbridge_driver.payload <= "00000100"; + wait until rising_edge(clk) and socbridge_driver_to_buffer.is_full_out = '0'; + wait until falling_edge(clk); + buffer_to_socbridge_driver.payload <= "00001000"; + wait until rising_edge(clk) and socbridge_driver_to_buffer.is_full_out = '0'; + wait until falling_edge(clk); + buffer_to_socbridge_driver.payload <= "00010000"; + wait until socbridge_driver_to_buffer.is_full_out = '0'; + wait for CLK_PERIOD/2; + wait until rising_edge(clk); + wait until rising_edge(clk); + buffer_to_socbridge_driver.payload <= "00100000"; + wait until socbridge_driver_to_buffer.is_full_out = '0'; + wait for CLK_PERIOD/2; + wait until rising_edge(clk); + wait until rising_edge(clk); --- ??? Why all these rising_edge checks? + wait; + end process internal_stimulus; + +end architecture tb ; diff --git a/src/socbridge/socbridge_driver_tb_pkg.vhd b/src/socbridge/socbridge_driver_tb_pkg.vhd new file mode 100644 index 0000000..fc8e8cd --- /dev/null +++ b/src/socbridge/socbridge_driver_tb_pkg.vhd @@ -0,0 +1,156 @@ +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use IEEE.MATH_REAL.all; +library ganimede; +use ganimede.io_types.all; + + +package socbridge_driver_tb_pkg is + subtype command_size_t is integer range 0 to 128; + + type command_t is + (NO_OP, WRITE_ADD, WRITE, READ_ADD, READ, P_ERR); + + type response_t is + (NO_OP, WRITE_ACK, READ_RESPONSE); + + type state_t is + (IDLE, ADDR1, ADDR2, ADDR3, ADDR4, + TX_HEADER, TX_BODY, TX_ACK, + RX_HEADER, RX_RESPONSE, RX_BODY); + + --- TRANSLATOR --- + type translator_state_t is (IDLE, SEND, SEND_ACCEPTED, AWAIT); + + type translator_state_rec_t is record + curr_inst : controller_to_socbridge_driver_t; + curr_state : translator_state_t; + is_first_word : std_logic; + end record translator_state_rec_t; + + type ext_protocol_t is record + data : std_logic_vector(interface_inst.socbridge.payload_width - 1 downto 0); + clk : std_logic; + parity : std_logic; + end record ext_protocol_t; + + type state_rec_t is record + curr_state: state_t; + ext_to_socbridge_driver_reg, socbridge_driver_to_ext_reg : ext_protocol_t; + write_stage, read_stage : NATURAL; + curr_cmd : command_t; + curr_cmd_size: integer; + curr_addr : std_logic_vector(31 downto 0); + end record state_rec_t; + impure function calc_parity( + d : STD_LOGIC_VECTOR(interface_inst.socbridge.payload_width - 1 downto 0) + ) return std_logic; + pure function create_io_type_out_from_ext_protocol( + input: ext_protocol_t + ) return socbridge_driver_to_ext_t; + function to_string ( a: std_logic_vector) return string; + pure function get_cmd_bits(command : command_t) return std_logic_vector; + pure function get_size_bits(size : command_size_t) return std_logic_vector; + pure function get_size_bits_sim(size : command_size_t) return std_logic_vector; + --- DEBUG GLOBAL SIGNALS --- + -- synthesis translate_off + signal G_next_parity_out : std_logic; + signal G_ext_to_socbridge_driver_rec : ext_protocol_t; + signal G_socbridge_driver_to_ext_data_cmd : std_logic_vector(interface_inst.socbridge.payload_width - 1 downto 0); + signal G_next_state : state_t; + signal G_curr_command : command_t; + signal G_curr_command_bits : std_logic_vector(4 downto 0); + signal G_curr_response : response_t; + signal G_curr_response_bits : std_logic_vector(4 downto 0); + signal G_st : state_rec_t; + signal G_trans_st : translator_state_rec_t; + -- synthesis translate_on + +end package socbridge_driver_tb_pkg; + +package body socbridge_driver_tb_pkg is + function to_string ( a: std_logic_vector) return string is + variable b : string (1 to a'length) := (others => NUL); + variable stri : integer := 1; + begin + for i in a'range loop + b(stri) := std_logic'image(a((i)))(2); + stri := stri+1; + end loop; + return b; + end function; + + impure function calc_parity( + d : STD_LOGIC_VECTOR(interface_inst.socbridge.payload_width - 1 downto 0) + ) return std_logic is + variable parity : std_logic; + begin + parity := d(0); + for x in integer'(1) to d'length - 1 loop + parity := parity xor d(x); + end loop; + return not parity; + end function; + + + + pure function create_io_type_out_from_ext_protocol( + input : ext_protocol_t + ) return socbridge_driver_to_ext_t is + variable val : socbridge_driver_to_ext_t; + begin + val.payload:= input.data; + val.control(1) := input.clk; + val.control(0) := input.parity; + return val; + end function; + pure function get_cmd_bits(command : command_t) + return std_logic_vector is + variable val : std_logic_vector(4 downto 0); + begin + with command select + val := "00000" when NO_OP, + "10000" when WRITE_ADD, + "10100" when WRITE, + "11000" when READ_ADD, + "11100" when READ, + "01001" when P_ERR, + "11111" when others; + return val; + end function; + + + pure function get_size_bits(size: command_size_t) + return std_logic_vector is + variable val : std_logic_vector(2 downto 0); + begin + if size > 2**6 then + val := "111"; + elsif size > 2**5 then + val := "110"; + elsif size > 2**4 then + val := "101"; + elsif size > 2**3 then + val := "100"; + elsif size > 2**2 then + val := "011"; + elsif size > 2**1 then + val := "010"; + elsif size > 2**0 then + val := "001"; + elsif size >= 0 then + val := "000"; + end if; + return val; + end function; + pure function get_size_bits_sim(size: command_size_t) + return std_logic_vector is + variable pow : integer; + variable val : std_logic_vector(2 downto 0); + begin + pow := integer(CEIL(sqrt(Real(size)))); + val := std_logic_vector(TO_UNSIGNED(size - 1, 3)); + return val; + end function; +end package body socbridge_driver_tb_pkg; diff --git a/src/test.vhd b/src/test.vhd deleted file mode 100644 index 5a27d24..0000000 --- a/src/test.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library IEEE; -library work; -use work.io_types.all; - -entity test is - port ( - ext_interface_in : in ext_interface_in_t; - ext_interface_out : out ext_interface_out_t - ); -end entity test; - -architecture rtl of test is - signal int_interface_in : int_interface_in_t; - signal int_interface_out : int_interface_out_t; -begin - - proc_name: process - begin - - report "Hello"; - report integer'image(ext_interface_in.socbridge.payload'length); - report integer'image(ext_interface_in.spi.payload'length); - wait; - end process proc_name; - - -end architecture rtl; diff --git a/src/vhdl_ls.toml b/src/vhdl_ls.toml new file mode 100644 index 0000000..3a01a68 --- /dev/null +++ b/src/vhdl_ls.toml @@ -0,0 +1,18 @@ +# What standard to use. This is optional and defaults to VHDL2008. +standard = "1993" +# File names are either absolute or relative to the parent folder of the vhdl_ls.toml file +[libraries] +ganimede.files = [ + 'ganimede/io_type_pkg.vhd' +] +socbridge.files = [ + 'socbridge/*.vhd' +] + +controller.files = [ + 'controller/*.vhd', +] + +[lint] +unused = 'error' # Upgrade the 'unused' diagnostic to the 'error' severity +unnecessary_work_library = false # Disable linting for the 'library work;' statement diff --git a/topology.md b/topology.md new file mode 100644 index 0000000..dc0b2a9 --- /dev/null +++ b/topology.md @@ -0,0 +1,32 @@ + + +------------------------------------------------------+ +--------------- + | | | + | +-----------------+ | | + *From host* | | Instruction | G A N I M E D E | | + --------------------------->| queue + | | | IP-CORE + | | scheduler | | | + | +-----------------+ | | + | *DMA control*| | | + | | | | + +--------+ | +--------+ | +-----------------+ | | + | Inter- | data + | | Driver |<---+ | Per interface | | data + | + | face 1 |<=================>| 1 |<===|===>| optimizations |<=============================>| + | | control | | | | | (generic) | | control | + +--------+ | +--------+ | +-----------------+ | | + | | | | + +--------+ | +--------+ | +-----------------+ | | + | Inter- | data + | | Driver |<---+ | Per interface | | data + | + | face 2 |<=================>| 2 |<===|===>| optimizations |<=============================>| + | | control | | | | | (generic) | | control | + +--------+ | +--------+ | +-----------------+ | | + ... ... ... ... ... ... ... ... ... ...|... . ... ... . ... ...... . ... ... . ... . | + +--------+ | +--------+ | +-----------------+ | | + | Inter- | data + | | Driver |<---+ | Per interface | | data + | + | face n |<=================>| n |<=======>| optimizations |<=============================>| + | | control | | | | (generic) | | control | + +--------+ | +--------+ +-----------------+ | | + | | | + +------------------------------------------------------+ +--------------- + + +This wont be the first version, but the finished version might look something like this.