The confusion probably comes from the formulation: "memory-mapped, cached view" The fact it is memory-mapped has nothing to do with the fact it is cached. The size of the memory mapping is independant of the size of the cache.
So, I'll break it down for you:
Means you can access the contents of the external memory directly by reading/writing the main memory address space (at some specified address). Also typically imply that, if the external memory contains executable code, you can execute this code simply by branching: you don't need to copy the code in internal memory before branching. This is achieved by the MCU which, internally, translates any access to this part of the memory into the required QSPI commands to read/write the external flash on the fly. At this point, it does not imply that there is a cache.
Means that data read from this part of the memory will be placed in a smaller-sized, intermediate memory area (not accessible directly), which the MCU will lookup first when the the external memory will have to be accessed again. This way, when the same data is accessed twice, the external memory does not need to be accessed again. The data from the cache will be retrieved, which is much faster.
Indeed, this is very useful for memory-mapped QSPI. The QSPI interface is much slower than the CPU: any read/write operation has to be translated into commands sent serially on a few signal lines, which adds a lot of overhead. To reduce this overhead, you'll typically try to read multiple bytes for each QSPI access, and store them in a cache so that, if the next read addresses the neighboring byte (which is likely), you have it ready.
Here, this is the size of the cache, not the size of the memory map. The size of the memory-map will typically be big enough for the whole size of the external memory (check the detailed specs).
This is the way the cache is internally organized. The cache is much smaller than the external memory. The naive way to implement a cache would be to store all the recently accessed bytes along with their corresponding addresses, and, when subsequent accesses are made, check in the whole cache if an existing byte has its address corresponding to the accessed address. This is extremely inefficient. For each byte, you would have to store the address, which multiplies by five the required size for the cache (assuming 32-bit addresses: for each byte, you need the data byte value plus four bytes for the corresponding address), and, for each access, you need to compare the address against 32768 possible values to check if it is already in the cache or not.
So, here is how it is done:
- First, the cache is organized in lines of N bytes (e.g. 16 or 32 bytes - Note that the cache line size is not specified in your case). You store the addresses for the whole cache lines, not for each byte, which saves a lot of space.
- Then, not all possible addresses can be stored anywhere in the cache. When accessing the cache, you take a part of the address, and this will give you the index of a "cache set". Each cache set can contain 4 cache lines (in your case). When checking if the data is in the cache, this means that you only have these 4 cache lines addresses to check because you know that, if it is there, it will necessarily be in this set. This will reduce the complexity of the cache structure a great deal, at the expense of less flexibility in storing the data (meaning a possibly lower cache hit rate, depending on the memory access patterns).
This is what the cache associativity is: The number of cache lines per set. This gives an indication of the likeliness you can retrieve data in the cache if it has been read before. The bigger the associativity, the better, but it makes the cache more complex and more expensive to manufacture. And at some point, the benefits are not even worth it. 4 is not bad (which is why they are proud to advertise it).