def activate_channel_with_cb(channel, cb_addr, do_start=True): if channel < 0 or channel > 15: raise Exception("Invalid channel index: {}".format(channel)) if channel < 15: ch_base = DMA_BASE_PHYS else: ch_base = DMA_BASE_CH15_PHYS ch_dma_cs = 0x100 * channel + DMA_CS ch_dma_debug = 0x100 * channel + DMA_DEBUG ch_dma_cb_ad = 0x100 * channel + DMA_CB_AD with mu.mmap_dev_mem(ch_base) as dma_mem: mu.write_word_to_byte_array(dma_mem, ch_dma_cs, DMA_CS_RESET) mu.write_word_to_byte_array(dma_mem, ch_dma_cs, DMA_CS_INT | DMA_CS_END) mu.write_word_to_byte_array(dma_mem, ch_dma_debug, DMA_DEBUG_CLR_ERRORS) mu.write_word_to_byte_array( dma_mem, ch_dma_cs, DMA_CS_WAIT_FOR_OUTSTANDING_WRITES | DMA_CS_PANIC_PRIORITY | DMA_CS_PRIORITY) mu.write_word_to_byte_array(dma_mem, ch_dma_cb_ad, cb_addr) if do_start: mu.write_word_to_byte_array(dma_mem, ch_dma_cs, DMA_CS_ACTIVE) time.sleep(0.1)
def test_activate_channel_with_cb_writes_cb_address_to_cb_address_register( self): channel = 2 dma.activate_channel_with_cb(channel, self.cb.addr, False) with mu.mmap_dev_mem(dma.DMA_BASE_PHYS) as m: channel_offset = 0x100 * channel cb_addr_register_offset = channel_offset + dma.DMA_CB_AD cb_addr_register = struct.unpack( '<L', m[cb_addr_register_offset:cb_addr_register_offset + 4])[0] self.assertEqual(self.cb.addr, cb_addr_register)
def test_virtual_to_physical_addr(self): initial_value = 0 dummy_item = ctypes.c_int(initial_value) virtual_address = ctypes.addressof(dummy_item) p_addr_info = mu.virtual_to_physical_addr(virtual_address) self.assertEqual(p_addr_info.frame_start + p_addr_info.offset, p_addr_info.p_addr) self.assertEqual(initial_value, dummy_item.value) new_value = 123 with mu.mmap_dev_mem(p_addr_info.frame_start) as m: m[p_addr_info.offset] = new_value self.assertEqual(new_value, dummy_item.value)
def set_pins_to_output(gpio_info_list): reg_offset_to_value_map = {} for gpio_info in gpio_info_list: reg_offset = gpio_info.gp_fsel_reg_offset current_value = reg_offset_to_value_map.get(reg_offset, 0) new_value = current_value | 1 << gpio_info.gp_fsel_bit_shift reg_offset_to_value_map[reg_offset] = new_value with mu.mmap_dev_mem(GPIO_BASE_PHYS) as m: for reg_offset in reg_offset_to_value_map.keys(): value = reg_offset_to_value_map[reg_offset] mu.write_word_to_byte_array(m, reg_offset, value) time.sleep(0.1)
def configure_and_start_pwm(dma_ch, pwm_clk_src, pwm_clk_div_int, pwm_clk_div_frac, pwm_cycles): if pwm_clk_src not in __allowed_clk_src_list: raise Exception("{} is not a valid clock source.".format(pwm_clk_src)) if pwm_clk_div_int < 2: raise Exception("Clock integer divider must be 2 or greater.") if pwm_clk_div_frac < 0 or pwm_clk_div_frac > 4095: raise Exception( "Clock fractional divider must be between 0 (inclusive) and 4095 (inclusive)." ) pwm_period_ns = 1000000000 / CLK_SRC_RATES[pwm_clk_src] * ( (pwm_clk_div_int + pwm_clk_div_frac / 4096) * pwm_cycles) print("Starting PWM with a period of " + str(pwm_period_ns) + " ns.") clk_cb = dma.ControlBlock() clk_cb.set_destination_addr(PWM_CLK_BASE_BUS) clk_cb.set_transfer_information(dma.DMA_TI_NO_WIDE_BURSTS | dma.DMA_TI_WAIT_RESP | dma.DMA_TI_SRC_INC | dma.DMA_TI_DEST_INC) # Stop and configure PWM clock clk_cb.set_transfer_length(8) clk_cb.write_word_to_source_data(PWM_CLK_CTL, PWM_CLK_PWD | pwm_clk_src) clk_cb.write_word_to_source_data( PWM_CLK_DIV, PWM_CLK_PWD | pwm_clk_div_frac | pwm_clk_div_int << 12) dma.activate_channel_with_cb(dma_ch, clk_cb.addr) time.sleep(0.1) # Start PWM clock clk_cb.write_word_to_source_data(PWM_CLK_CTL, PWM_CLK_PWD | pwm_clk_src | PWM_CLK_ENAB) dma.activate_channel_with_cb(dma_ch, clk_cb.addr) time.sleep(0.1) # Configure and start PWM with mu.mmap_dev_mem(PWM_BASE_PHYS) as m: mu.write_word_to_byte_array(m, PWM_CTL, 0) # Reset PWM mu.write_word_to_byte_array(m, PWM_RNG1, pwm_cycles) mu.write_word_to_byte_array(m, PWM_DMAC, PWM_DMAC_ENAB | PWM_DMAC_THRSHLD) mu.write_word_to_byte_array(m, PWM_CTL, PWM_CTL_CLRF) # Clear FIFO mu.write_word_to_byte_array( m, PWM_CTL, PWM_CTL_USEF1 | PWM_CTL_RPTL1 | PWM_CTL_PWEN1) time.sleep(0.1)
def test_set_pins_to_output_sets_correct_bits_of_correct_registers(self): with mu.mmap_dev_mem(gpio.GPIO_BASE_PHYS) as m: # Set all pins in GPFSEL1 register to input (000) and check that the write was successful gpfsel1_reg_offset = 0x4 mu.write_word_to_byte_array(m, gpfsel1_reg_offset, 0) gpfsel1_reg_value = struct.unpack( '<L', m[gpfsel1_reg_offset:gpfsel1_reg_offset + 4])[0] self.assertEqual(0, gpfsel1_reg_value) # Set pins 15 and 18 to output and check that the register bits are set as expected gpio_info15 = gpio.GpioInfo(15) gpio_info18 = gpio.GpioInfo(18) gpio.set_pins_to_output([gpio_info15, gpio_info18]) expected_gpfsel1_reg_value = 1 << 15 | 1 << 24 gpfsel1_reg_value = struct.unpack( '<L', m[gpfsel1_reg_offset:gpfsel1_reg_offset + 4])[0] self.assertEqual(expected_gpfsel1_reg_value, gpfsel1_reg_value)
def stop_pwm(dma_ch, pwm_clk_src): if pwm_clk_src not in __allowed_clk_src_list: raise Exception("{} is not a valid clock source.".format(pwm_clk_src)) # Reset PWM with mu.mmap_dev_mem(PWM_BASE_PHYS) as m: mu.write_word_to_byte_array(m, PWM_CTL, PWM_CTL_CLRF) # Clear FIFO mu.write_word_to_byte_array(m, PWM_CTL, 0) # Reset PWM time.sleep(0.1) # Stop PWM Clock clk_cb = dma.ControlBlock() clk_cb.set_destination_addr(PWM_CLK_BASE_BUS) clk_cb.set_transfer_information(dma.DMA_TI_NO_WIDE_BURSTS | dma.DMA_TI_WAIT_RESP | dma.DMA_TI_SRC_INC | dma.DMA_TI_DEST_INC) clk_cb.write_word_to_source_data(PWM_CLK_CTL, PWM_CLK_PWD | pwm_clk_src) dma.activate_channel_with_cb(dma_ch, clk_cb.addr) time.sleep(0.1)
def test_activate_channel_with_cb_resets_debug_register_and_clears_errors( self): channel = 2 dma.activate_channel_with_cb(channel, self.cb.addr, False) with mu.mmap_dev_mem(dma.DMA_BASE_PHYS) as m: channel_offset = 0x100 * channel debug_register_offset = channel_offset + dma.DMA_DEBUG debug_register = struct.unpack( '<L', m[debug_register_offset:debug_register_offset + 4])[0] self.assertFalse(is_nth_bit_set(debug_register, 0)) # No read last not set error self.assertFalse(is_nth_bit_set(debug_register, 1)) # No FIFO error self.assertFalse(is_nth_bit_set(debug_register, 2)) # No slave response error # Zero outstanding writes self.assertFalse(is_nth_bit_set(debug_register, 4)) self.assertFalse(is_nth_bit_set(debug_register, 5)) self.assertFalse(is_nth_bit_set(debug_register, 6)) self.assertFalse(is_nth_bit_set(debug_register, 7))
def test_activate_channel_with_cb_resets_cs_register_and_sets_defaults( self): channel = 2 dma.activate_channel_with_cb(channel, self.cb.addr, False) with mu.mmap_dev_mem(dma.DMA_BASE_PHYS) as m: channel_offset = 0x100 * channel cs_register_offset = channel_offset + dma.DMA_CS cs_register = struct.unpack( '<L', m[cs_register_offset:cs_register_offset + 4])[0] self.assertFalse(is_nth_bit_set(cs_register, 0)) # Not active self.assertFalse(is_nth_bit_set( cs_register, 1)) # No control block has been completed self.assertFalse(is_nth_bit_set( cs_register, 2)) # Channel has not produced an interrupt self.assertFalse(is_nth_bit_set( cs_register, 4)) # Not paused, because it hasn't started yet self.assertFalse(is_nth_bit_set(cs_register, 5)) # Not paused by DREQ self.assertFalse(is_nth_bit_set( cs_register, 6)) # Not waiting for any writes to complete self.assertFalse(is_nth_bit_set(cs_register, 8)) # No errors detected # AXI Priority is 8 self.assertFalse(is_nth_bit_set(cs_register, 16)) self.assertFalse(is_nth_bit_set(cs_register, 17)) self.assertFalse(is_nth_bit_set(cs_register, 18)) self.assertTrue(is_nth_bit_set(cs_register, 19)) # AXI Panic Priority is 8 self.assertFalse(is_nth_bit_set(cs_register, 20)) self.assertFalse(is_nth_bit_set(cs_register, 21)) self.assertFalse(is_nth_bit_set(cs_register, 22)) self.assertTrue(is_nth_bit_set(cs_register, 23)) self.assertTrue(is_nth_bit_set( cs_register, 28)) # Channel configured to wait for outstanding writes self.assertFalse(is_nth_bit_set( cs_register, 29)) # Debug pause signal is honored
def __init__(self, num_leds, gpio_pins: [gpio.GpioInfo]): self.dma_data = fd.LedDmaFrameData(num_leds) # SET and CLR registers are spaced as follows, spanning 5 registers: # SET SET -- CLR CLR # 1C 20 24 28 2C # MS_MBOX_0 - MS_MBOX_7 are peripheral registers usable for storing this data. # We will only need MS_MBOX_0 - MS_MBOX_4. # The first two registers will be used to always set / clear gpio pins, so the appropriate bits in MS_MBOX_0 and # MS_MBOX_1 should just statically be set for this purpose. MS_MBOX_3 and MS_MBOX_4 will be used for optionally # clearing the GPIO pins. with mu.mmap_dev_mem(MS_BASE) as m: mu.write_word_to_byte_array( m, MS_MBOX_REG_OFFSET + GPIO_INFO_PIN18.set_clr_register_index, 1 << GPIO_INFO_PIN18.pin_flip_bit_shift | 1 << GPIO_INFO_PIN15.pin_flip_bit_shift) # Allocate enough memory for all the CBs. self.shared_mem = mu.create_aligned_phys_contig_int_view(32, 32) # CBs self.cb_idle_wait = dma.ControlBlock() self.cb_idle_clr = dma.ControlBlock() self.cb_data_advance = dma.ControlBlock( self.shared_mem, 0) # Advances own SRC_ADDR and cb_data_upd's SRC_ADDR self.cb_data_upd = dma.ControlBlock( self.shared_mem, 24) # Writes next bit to be copied to GPIO into MS_MBOX self.cb_data_wait1 = dma.ControlBlock() self.cb_data_set_clr = dma.ControlBlock() self.cb_data_wait2 = dma.ControlBlock() self.cb_data_clr = dma.ControlBlock( self.shared_mem, 8) # Clears GPIO pins. Goes to cb_data_advance or cb_pause self.cb_pause = dma.ControlBlock( ) # Resets cb_idle_clr's NEXT_CB_ADDR to get into the idle CB loop # Configure idle loop self.cb_idle_wait.set_transfer_information(DMA_FLAGS_PWM) self.cb_idle_wait.set_destination_addr(pwm.PWM_BASE_BUS + pwm.PWM_FIFO) self.cb_idle_wait.set_next_cb_addr(self.cb_idle_clr.addr) self.cb_idle_clr.set_transfer_information(dma.DMA_TI_SRC_INC | dma.DMA_TI_DEST_INC) self.cb_idle_clr.set_transfer_length(8) self.cb_idle_clr.set_source_addr(MS_BASE_BUS + MS_MBOX_REG_OFFSET) self.cb_idle_clr.set_destination_addr(gpio.GPIO_BASE_BUS + gpio.GPCLR0) self.cb_idle_clr.set_next_cb_addr(self.cb_idle_wait.addr) # Configure data loop cb_data_advance_src_addr = self.cb_data_advance.addr + 0x4 src_stride = 4 dest_stride = 48 self.cb_data_advance.set_transfer_information(dma.DMA_TI_TD_MODE) self.cb_data_advance.set_source_addr(self.dma_data.start_address) self.cb_data_advance.set_destination_addr(cb_data_advance_src_addr) self.cb_data_advance.set_transfer_length_stride(4, 3) self.cb_data_advance.set_stride(src_stride, dest_stride) self.cb_data_advance.set_next_cb_addr(self.cb_data_upd.addr) # writes GPIO CLR data to MS_MBOX_3,4 self.cb_data_upd.set_transfer_information(dma.DMA_TI_SRC_INC | dma.DMA_TI_DEST_INC) self.cb_data_upd.set_transfer_length(8) self.cb_data_upd.set_destination_addr(MS_BASE_BUS + MS_MBOX_REG_OFFSET + 12) self.cb_data_upd.set_next_cb_addr(self.cb_data_wait1.addr) self.cb_data_wait1.set_transfer_information(DMA_FLAGS_PWM) self.cb_data_wait1.set_destination_addr(pwm.PWM_BASE_BUS + pwm.PWM_FIFO) self.cb_data_wait1.set_next_cb_addr(self.cb_data_set_clr.addr) self.cb_data_set_clr.set_transfer_information(dma.DMA_TI_NO_WIDE_BURSTS | dma.DMA_TI_DEST_INC | dma.DMA_TI_SRC_INC | DMA_WAITS) self.cb_data_set_clr.set_transfer_length(20) self.cb_data_set_clr.set_source_addr(MS_BASE_BUS + MS_MBOX_REG_OFFSET) self.cb_data_set_clr.set_destination_addr(gpio.GPIO_BASE_BUS + gpio.GPSET0) self.cb_data_set_clr.set_next_cb_addr(self.cb_data_wait2.addr) self.cb_data_wait2.set_transfer_information(DMA_FLAGS_PWM) self.cb_data_wait2.set_destination_addr(pwm.PWM_BASE_BUS + pwm.PWM_FIFO) self.cb_data_wait2.set_next_cb_addr(self.cb_data_clr.addr) self.cb_data_clr.set_transfer_information(dma.DMA_TI_NO_WIDE_BURSTS | dma.DMA_TI_SRC_INC | dma.DMA_TI_DEST_INC) self.cb_data_clr.set_transfer_length(8) self.cb_data_clr.set_source_addr(MS_BASE_BUS + MS_MBOX_REG_OFFSET) self.cb_data_clr.set_destination_addr(gpio.GPIO_BASE_BUS + gpio.GPCLR0) self.cb_pause.set_transfer_length(4) self.cb_pause.write_word_to_source_data(0, self.cb_idle_wait.addr) self.cb_pause.set_destination_addr(self.cb_idle_clr.addr + 0x14) self.cb_pause.set_next_cb_addr(self.cb_idle_clr.addr) self.dma_data.set_cb_addrs(self.cb_data_advance.addr, self.cb_pause.addr) self.gpio_pins = gpio_pins