1 /*
2  * SPDX-FileCopyrightText: Copyright 2019-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the License); you may
6  * not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
13  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /******************************************************************************
19  * Includes
20  ******************************************************************************/
21 #include "ethosu85_interface.h"
22 
23 #include "ethosu_config_u85.h"
24 #include "ethosu_device.h"
25 #include "ethosu_log.h"
26 
27 #include <assert.h>
28 #include <inttypes.h>
29 #include <stdbool.h>
30 #include <stddef.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 /******************************************************************************
35  * Defines
36  ******************************************************************************/
37 
38 #define ETHOSU_PRODUCT_U85 2
39 
40 #define BASEP_OFFSET 4
41 
42 #define ADDRESS_BITS 40
43 
44 #define ADDRESS_MASK ((1ull << ADDRESS_BITS) - 1)
45 
46 #define NPU_CMD_PWR_CLK_MASK (0xC)
47 #define NPU_MAC_PWR_RAMP_CYCLES_MASK (0x3F)
48 
49 /******************************************************************************
50  * Functions
51  ******************************************************************************/
52 
ethosu_address_remap(uint64_t address,int index)53 uint64_t __attribute__((weak)) ethosu_address_remap(uint64_t address, int index)
54 {
55     (void)(index);
56     return address;
57 }
58 
ethosu_dev_init(struct ethosu_device * dev,void * base_address,uint32_t secure_enable,uint32_t privilege_enable)59 bool ethosu_dev_init(struct ethosu_device *dev, void *base_address, uint32_t secure_enable, uint32_t privilege_enable)
60 {
61     dev->reg        = (volatile struct NPU_REG *)base_address;
62     dev->secure     = secure_enable;
63     dev->privileged = privilege_enable;
64 
65     if (dev->reg->CONFIG.product != ETHOSU_PRODUCT_U85)
66     {
67         LOG_ERR("Failed to initialize device. Driver has not been compiled for this product");
68         return false;
69     }
70 
71     // Make sure the NPU is in a known state
72     if (ethosu_dev_soft_reset(dev) != ETHOSU_SUCCESS)
73     {
74         return false;
75     }
76 
77     return true;
78 }
79 
ethosu_dev_axi_init(struct ethosu_device * dev)80 enum ethosu_error_codes ethosu_dev_axi_init(struct ethosu_device *dev)
81 {
82     struct regioncfg_r rcfg = {0};
83     struct axi_sram_r axi_s = {0};
84     struct axi_ext_r axi_e  = {0};
85 
86     // Configure MEM_ATTR array. These are user configurable,
87     // and each region will be set to use one of the entries
88     // as its config.
89     dev->reg->MEM_ATTR[0].word = NPU_MEM_ATTR_0;
90     dev->reg->MEM_ATTR[1].word = NPU_MEM_ATTR_1;
91     dev->reg->MEM_ATTR[2].word = NPU_MEM_ATTR_2;
92     dev->reg->MEM_ATTR[3].word = NPU_MEM_ATTR_3;
93 
94     // Set MEM_ATTR entry for command stream
95     dev->reg->QCONFIG.word = NPU_QCONFIG;
96 
97     // Set MEM_ATTR entries to use for regions 0-7
98     rcfg.region0             = NPU_REGIONCFG_0;
99     rcfg.region1             = NPU_REGIONCFG_1;
100     rcfg.region2             = NPU_REGIONCFG_2;
101     rcfg.region3             = NPU_REGIONCFG_3;
102     rcfg.region4             = NPU_REGIONCFG_4;
103     rcfg.region5             = NPU_REGIONCFG_5;
104     rcfg.region6             = NPU_REGIONCFG_6;
105     rcfg.region7             = NPU_REGIONCFG_7;
106     dev->reg->REGIONCFG.word = rcfg.word;
107 
108     // Set AXI limits on SRAM AXI interfaces
109     axi_s.max_outstanding_read_m1  = AXI_LIMIT_SRAM_MAX_OUTSTANDING_READ_M1 - 1;
110     axi_s.max_outstanding_write_m1 = AXI_LIMIT_SRAM_MAX_OUTSTANDING_WRITE_M1 - 1;
111     axi_s.max_beats                = AXI_LIMIT_SRAM_MAX_BEATS;
112     dev->reg->AXI_SRAM.word        = axi_s.word;
113 
114     // Set AXI limits on EXT AXI interface(s)
115     axi_e.max_outstanding_read_m1  = AXI_LIMIT_EXT_MAX_OUTSTANDING_READ_M1 - 1;
116     axi_e.max_outstanding_write_m1 = AXI_LIMIT_EXT_MAX_OUTSTANDING_WRITE_M1 - 1;
117     axi_e.max_beats                = AXI_LIMIT_EXT_MAX_BEATS;
118     dev->reg->AXI_EXT.word         = axi_e.word;
119 
120     return ETHOSU_SUCCESS;
121 }
122 
ethosu_dev_run_command_stream(struct ethosu_device * dev,const uint8_t * cmd_stream_ptr,uint32_t cms_length,const uint64_t * base_addr,int num_base_addr)123 void ethosu_dev_run_command_stream(struct ethosu_device *dev,
124                                    const uint8_t *cmd_stream_ptr,
125                                    uint32_t cms_length,
126                                    const uint64_t *base_addr,
127                                    int num_base_addr)
128 {
129     assert(num_base_addr <= NPU_REG_BASEP_ARRLEN);
130 
131     struct cmd_r cmd;
132     uint64_t qbase = ethosu_address_remap((uintptr_t)cmd_stream_ptr, -1);
133     assert(qbase <= ADDRESS_MASK);
134     LOG_DEBUG("QBASE=0x%016llx, QSIZE=%" PRIu32 ", cmd_stream_ptr=%p", qbase, cms_length, cmd_stream_ptr);
135 
136     dev->reg->QBASE.word[0] = qbase & 0xffffffff;
137     dev->reg->QBASE.word[1] = qbase >> 32;
138     dev->reg->QSIZE.word    = cms_length;
139 
140     for (int i = 0; i < num_base_addr; i++)
141     {
142         uint64_t addr = ethosu_address_remap(base_addr[i], i);
143         assert(addr <= ADDRESS_MASK);
144         LOG_DEBUG("BASEP%d=0x%016llx", i, addr);
145         dev->reg->BASEP[i].word[0] = addr & 0xffffffff;
146         dev->reg->BASEP[i].word[1] = addr >> 32;
147     }
148 
149     cmd.word                        = dev->reg->CMD.word & NPU_CMD_PWR_CLK_MASK;
150     cmd.transition_to_running_state = 1;
151 
152     dev->reg->CMD.word = cmd.word;
153     LOG_DEBUG("CMD=0x%08" PRIx32, cmd.word);
154 }
155 
ethosu_dev_print_err_status(struct ethosu_device * dev)156 void ethosu_dev_print_err_status(struct ethosu_device *dev)
157 {
158     LOG_ERR("NPU status=0x%08" PRIx32 ", qread=%" PRIu32 ", cmd_end_reached=%u",
159             dev->reg->STATUS.word,
160             dev->reg->QREAD.word,
161             dev->reg->STATUS.cmd_end_reached);
162 }
163 
ethosu_dev_handle_interrupt(struct ethosu_device * dev)164 bool ethosu_dev_handle_interrupt(struct ethosu_device *dev)
165 {
166     struct cmd_r cmd;
167 
168     // Clear interrupt
169     cmd.word           = dev->reg->CMD.word & NPU_CMD_PWR_CLK_MASK;
170     cmd.clear_irq      = 1;
171     dev->reg->CMD.word = cmd.word;
172 
173     // If a fault has occured, the NPU needs to be reset
174     if (dev->reg->STATUS.bus_status || dev->reg->STATUS.cmd_parse_error || dev->reg->STATUS.branch_fault ||
175         dev->reg->STATUS.ecc_fault || !dev->reg->STATUS.cmd_end_reached)
176     {
177         return false;
178     }
179 
180     return true;
181 }
182 
ethosu_dev_verify_access_state(struct ethosu_device * dev)183 bool ethosu_dev_verify_access_state(struct ethosu_device *dev)
184 {
185     if (dev->reg->PROT.active_CSL != (dev->secure ? SECURITY_LEVEL_SECURE : SECURITY_LEVEL_NON_SECURE) ||
186         dev->reg->PROT.active_CPL != (dev->privileged ? PRIVILEGE_LEVEL_PRIVILEGED : PRIVILEGE_LEVEL_USER))
187     {
188         return false;
189     }
190     return true;
191 }
192 
ethosu_dev_soft_reset(struct ethosu_device * dev)193 enum ethosu_error_codes ethosu_dev_soft_reset(struct ethosu_device *dev)
194 {
195     struct reset_r reset;
196 
197     reset.word        = 0;
198     reset.pending_CPL = dev->privileged ? PRIVILEGE_LEVEL_PRIVILEGED : PRIVILEGE_LEVEL_USER;
199     reset.pending_CSL = dev->secure ? SECURITY_LEVEL_SECURE : SECURITY_LEVEL_NON_SECURE;
200 
201     // Reset and set security level
202     LOG_INFO("Soft reset NPU");
203     dev->reg->RESET.word = reset.word;
204 
205     // Wait until reset status indicates that reset has been completed
206     for (int i = 0; i < 100000 && dev->reg->STATUS.reset_status != 0; i++)
207     {
208     }
209 
210     if (dev->reg->STATUS.reset_status != 0)
211     {
212         LOG_ERR("Soft reset timed out");
213         return ETHOSU_GENERIC_FAILURE;
214     }
215 
216     // Verify that NPU has switched security state and privilege level
217     if (ethosu_dev_verify_access_state(dev) != true)
218     {
219         LOG_ERR("Failed to switch security state and privilege level");
220         return ETHOSU_GENERIC_FAILURE;
221     }
222 
223     // Reinitialize AXI settings
224     ethosu_dev_axi_init(dev);
225 
226     // MAC power ramping up/down control
227     dev->reg->POWER_CTRL.word = (NPU_MAC_PWR_RAMP_CYCLES & NPU_MAC_PWR_RAMP_CYCLES_MASK);
228 
229     return ETHOSU_SUCCESS;
230 }
231 
ethosu_dev_get_hw_info(struct ethosu_device * dev,struct ethosu_hw_info * hwinfo)232 void ethosu_dev_get_hw_info(struct ethosu_device *dev, struct ethosu_hw_info *hwinfo)
233 {
234     struct config_r cfg;
235     struct id_r id;
236 
237     cfg.word = dev->reg->CONFIG.word;
238     id.word  = dev->reg->ID.word;
239 
240     hwinfo->cfg.cmd_stream_version = cfg.cmd_stream_version;
241     hwinfo->cfg.custom_dma         = cfg.custom_dma;
242     hwinfo->cfg.macs_per_cc        = cfg.macs_per_cc;
243 
244     hwinfo->version.arch_major_rev = id.arch_major_rev;
245     hwinfo->version.arch_minor_rev = id.arch_minor_rev;
246     hwinfo->version.arch_patch_rev = id.arch_patch_rev;
247     hwinfo->version.product_major  = id.product_major;
248     hwinfo->version.version_major  = id.version_major;
249     hwinfo->version.version_minor  = id.version_minor;
250     hwinfo->version.version_status = id.version_status;
251 }
252 
ethosu_dev_set_clock_and_power(struct ethosu_device * dev,enum ethosu_clock_q_request clock_q,enum ethosu_power_q_request power_q)253 enum ethosu_error_codes ethosu_dev_set_clock_and_power(struct ethosu_device *dev,
254                                                        enum ethosu_clock_q_request clock_q,
255                                                        enum ethosu_power_q_request power_q)
256 {
257     struct cmd_r cmd = {0};
258     cmd.word         = dev->reg->CMD.word & NPU_CMD_PWR_CLK_MASK;
259 
260     if (power_q != ETHOSU_POWER_Q_UNCHANGED)
261     {
262         cmd.power_q_enable = power_q == ETHOSU_POWER_Q_ENABLE ? 1 : 0;
263     }
264     if (clock_q != ETHOSU_CLOCK_Q_UNCHANGED)
265     {
266         cmd.clock_q_enable = clock_q == ETHOSU_CLOCK_Q_ENABLE ? 1 : 0;
267     }
268 
269     dev->reg->CMD.word = cmd.word;
270     LOG_DEBUG("CMD=0x%08" PRIx32, cmd.word);
271 
272     return ETHOSU_SUCCESS;
273 }
274 
ethosu_dev_verify_optimizer_config(struct ethosu_device * dev,uint32_t cfg_in,uint32_t id_in)275 bool ethosu_dev_verify_optimizer_config(struct ethosu_device *dev, uint32_t cfg_in, uint32_t id_in)
276 {
277     struct config_r *opt_cfg = (struct config_r *)&cfg_in;
278     struct config_r hw_cfg;
279     struct id_r *opt_id = (struct id_r *)&id_in;
280     struct id_r hw_id;
281     bool ret = true;
282 
283     hw_cfg.word = dev->reg->CONFIG.word;
284     hw_id.word  = dev->reg->ID.word;
285 
286     LOG_INFO("Optimizer config. product=%u, cmd_stream_version=%u, macs_per_cc=%u, num_axi_ext=%u, num_axi_sram=%u, "
287              "custom_dma=%u",
288              opt_cfg->product,
289              opt_cfg->cmd_stream_version,
290              opt_cfg->macs_per_cc,
291              1U << opt_cfg->num_axi_ext,
292              1U << opt_cfg->num_axi_sram,
293              opt_cfg->custom_dma);
294 
295     LOG_INFO("Optimizer config. arch version=%u.%u.%u",
296              opt_id->arch_major_rev,
297              opt_id->arch_minor_rev,
298              opt_id->arch_patch_rev);
299 
300     LOG_INFO("Ethos-U config. product=%u, cmd_stream_version=%u, macs_per_cc=%u, num_axi_ext=%u, num_axi_sram=%u, "
301              "custom_dma=%u",
302              hw_cfg.product,
303              hw_cfg.cmd_stream_version,
304              hw_cfg.macs_per_cc,
305              1U << hw_cfg.num_axi_ext,
306              1U << hw_cfg.num_axi_sram,
307              hw_cfg.custom_dma);
308 
309     LOG_INFO("Ethos-U. arch version=%u.%u.%u", hw_id.arch_major_rev, hw_id.arch_minor_rev, hw_id.arch_patch_rev);
310 
311     if (opt_cfg->word != hw_cfg.word)
312     {
313         if (hw_cfg.product != opt_cfg->product)
314         {
315             LOG_ERR("NPU config mismatch. npu.product=%u, optimizer.product=%u", hw_cfg.product, opt_cfg->product);
316             ret = false;
317         }
318 
319         if (hw_cfg.macs_per_cc != opt_cfg->macs_per_cc)
320         {
321             LOG_ERR("NPU config mismatch. npu.macs_per_cc=%u, optimizer.macs_per_cc=%u",
322                     hw_cfg.macs_per_cc,
323                     opt_cfg->macs_per_cc);
324             ret = false;
325         }
326 
327         if (hw_cfg.num_axi_ext != opt_cfg->num_axi_ext)
328         {
329             LOG_ERR("NPU config mismatch. npu.num_axi_ext=%u, optimizer.num_axi_ext=%u",
330                     1U << hw_cfg.num_axi_ext,
331                     1U << opt_cfg->num_axi_ext);
332             ret = false;
333         }
334 
335         if (hw_cfg.num_axi_sram != opt_cfg->num_axi_sram)
336         {
337             LOG_ERR("NPU config mismatch. npu.num_axi_sram=%u, optimizer.num_axi_sram=%u",
338                     1U << hw_cfg.num_axi_sram,
339                     1U << opt_cfg->num_axi_sram);
340             ret = false;
341         }
342 
343         if (hw_cfg.cmd_stream_version != opt_cfg->cmd_stream_version)
344         {
345             LOG_ERR("NPU config mismatch. npu.cmd_stream_version=%u, optimizer.cmd_stream_version=%u",
346                     hw_cfg.cmd_stream_version,
347                     opt_cfg->cmd_stream_version);
348             ret = false;
349         }
350 
351         if (!hw_cfg.custom_dma && opt_cfg->custom_dma)
352         {
353             LOG_ERR("NPU config mismatch. npu.custom_dma=%u, optimizer.custom_dma=%u",
354                     hw_cfg.custom_dma,
355                     opt_cfg->custom_dma);
356             ret = false;
357         }
358     }
359 
360     if ((hw_id.arch_major_rev != opt_id->arch_major_rev) || (hw_id.arch_minor_rev < opt_id->arch_minor_rev))
361     {
362         LOG_ERR("NPU arch mismatch. npu.arch=%u.%u.%u, optimizer.arch=%u.%u.%u",
363                 hw_id.arch_major_rev,
364                 hw_id.arch_minor_rev,
365                 hw_id.arch_patch_rev,
366                 opt_id->arch_major_rev,
367                 opt_id->arch_minor_rev,
368                 opt_id->arch_patch_rev);
369         ret = false;
370     }
371 
372     return ret;
373 }
374